[LyX/master] add short language switch (file format change)

Udi Fogiel <[email protected]> Sun, 12 Jul 2026 01:37:52 +0000
Newsgroups gmane.editors.lyx.cvs
Message-ID <[email protected]>
commit 4bb5644d4ba9807859f7e38aa50e0cab388084da
Author: Udi Fogiel <[email protected]>
Date:   Sun Jul 12 01:34:11 2026 +0300

    add short language switch (file format change)
    
    LyX infers from context when to use a short or a long language
    switch (\selectlanguage vs \foreignlanguage). For example, it chooses
    to use \selectlanguage{<LANG>} at pargraph start according to the paragraph
    first character.
    
    This means that in a situation where a user wants to have a short
    text of a foreign language at the beginning of the paragrph,
    the language switch affect the whole paragraph, and the user has no
    control over this choice.
    
    This commit adds a new font/language property that indicates that
    this characters should always use short language switch. Characters
    with such properties are ignored when LyX searches characters in the paragrph
    to determine the paragraph language.
    
    To indicate that a language switch should be short, the language LFUN
    was extended to accept a new optional "short" keyword. Also a new checkbox
    was added top the text properties dialog.
    
    Inside the LyX file, a short language s indicated by having the "short"
    keyword after the language name (e.g. \lang german short).  When reverted
    to older LyX files, this short keyword is stripped, and if a short language
    is at the start of a paragraph a LyX note is added to keep the LaTeX output faithful
    (this LyX note with the current languae blocks the first character lookup).
    
    This should fix tickets #2190 and #3660.
---
 development/FORMAT                 |  5 +++
 lib/RELEASE-NOTES                  |  3 ++
 lib/doc/UserGuide.lyx              | 31 ++++++++++++----
 lib/lyx2lyx/lyx_2_6.py             | 72 +++++++++++++++++++++++++++++++++++++-
 src/Cursor.cpp                     | 12 ++++++-
 src/Font.cpp                       | 17 ++++++---
 src/FontInfo.cpp                   | 12 ++++++-
 src/FontInfo.h                     | 13 +++++--
 src/LyXAction.cpp                  |  5 ++-
 src/Paragraph.cpp                  |  9 +++--
 src/Text.cpp                       | 33 ++++++++++++++---
 src/frontends/qt/GuiCharacter.cpp  | 21 ++++++++++-
 src/frontends/qt/GuiCharacter.h    |  3 ++
 src/frontends/qt/ui/CharacterUi.ui | 10 ++++++
 src/version.h                      |  4 +--
 15 files changed, 222 insertions(+), 28 deletions(-)

diff --git a/development/FORMAT b/development/FORMAT
index 7b77fbe937..8a1d316816 100644
--- a/development/FORMAT
+++ b/development/FORMAT
@@ -7,6 +7,11 @@ changes happened in particular if possible. A good example would be
 
 -----------------------
 
+2026-07-11 Udi Fogiel <[email protected]>
+	* Format incremented to 651:
+	  New optional "short" keyword on the \lang font token (\lang <LANG> short),
+	  marking a language switch that will always be rendered as a short switch.
+
 2026-04-24 Jürgen Spitzmüller <[email protected]>
 	* Format incremented to 650:
 	  New \language_package value "polyglossia" which uses polyglossia whenever
diff --git a/lib/RELEASE-NOTES b/lib/RELEASE-NOTES
index b6fe35c956..b7ae6fb491 100644
--- a/lib/RELEASE-NOTES
+++ b/lib/RELEASE-NOTES
@@ -35,6 +35,9 @@
 - The function newpage-insert has been renamed to textbreak-insert
   (since it does not only insert page breaks)
 
+- The function language now accepts an additional "short" argument
+  which ensures the language switch is a short one (e.g. \foreignlanguage).
+  
 !!!The following LyX functions have been removed in 2.6:
 
 !!!The following LyX function has been added and then removed in 2.6 development cycle:
diff --git a/lib/doc/UserGuide.lyx b/lib/doc/UserGuide.lyx
index 6a7c81bd8a..473a31ee01 100644
--- a/lib/doc/UserGuide.lyx
+++ b/lib/doc/UserGuide.lyx
@@ -1,5 +1,5 @@
-#LyX 2.5 created this file. For more info see https://www.lyx.org/
-\lyxformat 643
+#LyX 2.6 created this file. For more info see https://www.lyx.org/
+\lyxformat 651
 \begin_document
 \begin_header
 \save_transient_properties true
@@ -161,7 +161,7 @@ logicalmkup
 \end_branch
 \index Index
 \shortcut idx
-\color #008000
+\color #ff008000
 \end_index
 \secnumdepth 3
 \tocdepth 2
@@ -186,6 +186,7 @@ logicalmkup
 \docbook_mathml_prefix 1
 \docbook_mathml_version 0
 \author -712698321 "Jürgen Spitzmüller"
+\author 1064312605 "Udi Fogiel"
 \end_header
 
 \begin_body
@@ -17405,6 +17406,22 @@ Exclude from Spellchecking
 \family default
 .
  Passages marked like this will still be hyphenated correctly.
+\change_inserted 1064312605 1783806993
+
+\begin_inset Newline newline
+\end_inset
+
+If you want to switch the language of a short passage without ever letting it become the language of the whole paragraph —
+ even if the passage happens to start at the very beginning of the paragraph —
+ activate 
+\family sans
+Short Language Switch
+\family default
+.
+ This is useful e. g.
+ for a single foreign word or short quotation inserted inline.
+\change_unchanged
+
 \end_layout
 
 \begin_layout Standard
@@ -39694,7 +39711,7 @@ literal "true"
 \end_layout
 
 \begin_layout Standard
-\begin_inset Newpage clearpage
+\begin_inset Textbreak clearpage
 \end_inset
 
 
@@ -40784,7 +40801,7 @@ literal "true"
 \end_layout
 
 \begin_layout Standard
-\begin_inset Newpage clearpage
+\begin_inset Textbreak clearpage
 \end_inset
 
 
@@ -58266,7 +58283,7 @@ textwidth
 \end_layout
 
 \begin_layout Standard
-\begin_inset Newpage newpage
+\begin_inset Textbreak newpage
 \end_inset
 
 The bibliography on the following page was created with the 
@@ -59657,7 +59674,7 @@ https://wiki.lyx.org/Windows/Japanese
 \end_layout
 
 \begin_layout Standard
-\begin_inset Newpage newpage
+\begin_inset Textbreak newpage
 \end_inset
 
 
diff --git a/lib/lyx2lyx/lyx_2_6.py b/lib/lyx2lyx/lyx_2_6.py
index 9aacfb75bf..47d438c560 100644
--- a/lib/lyx2lyx/lyx_2_6.py
+++ b/lib/lyx2lyx/lyx_2_6.py
@@ -887,6 +887,74 @@ def revert_shorthands2(document):
         continue
 
 
+def revert_lang_short(document):
+    """Revert short language switches.
+
+    short language switches (which appears as \\lang <LANG short>),
+    marking a language switch that must always render as a short switch.
+    if such a switch happens to be the first content of its
+    paragraph, older LyX would (wrongly) treat it as changing the
+    whole paragraph's language. Guard against that by inserting an
+    empty Note inset before it (idea from #3660), so the switch
+    is no longer at the paragraph's first position,
+    then drop the keyword."""
+    font_attr_tokens = (
+        "\\family",
+        "\\series",
+        "\\shape",
+        "\\size",
+        "\\emph",
+        "\\numeric",
+        "\\nospellcheck",
+        "\\bar",
+        "\\strikeout",
+        "\\xout",
+        "\\uuline",
+        "\\uwave",
+        "\\noun",
+        "\\color",
+        "\\lang",
+    )
+
+    def is_paragraph_initial(i):
+        layout = get_containing_layout(document.body, i)
+        if not layout:
+            return False
+        for j in range(layout[3], i):
+            line = document.body[j].strip()
+            if line and line.split(" ", 1)[0] not in font_attr_tokens:
+                return False
+        return True
+
+    i = 0
+    while True:
+        i = find_token(document.body, "\\lang ", i)
+        if i == -1:
+            break
+        line = document.body[i]
+        if not line.rstrip().endswith(" short"):
+            i += 1
+            continue
+        if is_paragraph_initial(i):
+            note = [
+                "\\begin_inset Note Note",
+                "status collapsed",
+                "",
+                "\\begin_layout Plain Layout",
+                "",
+                "\\end_layout",
+                "",
+                "\\end_inset",
+                "",
+                "",
+            ]
+            document.body[i:i] = note
+            i += len(note)
+            line = document.body[i]
+        document.body[i] = line.rstrip()[: -len(" short")]
+        i += 1
+
+
 def revert_lp_polyglossia(document):
     """Revert language package polyglossia to auto"""
     i = find_token(document.header, "\\language_package polyglossia", 0)
@@ -905,11 +973,13 @@ convert = [
     [647, [convert_textbreaks]],
     [648, []],
     [649, []],
-    [650, []]
+    [650, []],
+    [651, []]
 ]
 
 
 revert = [
+    [650, [revert_lang_short]],
     [649, [revert_lp_polyglossia]],
     [648, [revert_shorthands2]],
     [647, [revert_hyphen_shorthands]],
diff --git a/src/Cursor.cpp b/src/Cursor.cpp
index 2b713a80f5..e6a3d57def 100644
--- a/src/Cursor.cpp
+++ b/src/Cursor.cpp
@@ -2536,6 +2536,14 @@ void Cursor::setLanguageFromInput()
 	string const & code = theApp()->inputLanguageCode();
 	Language const * lang = languages.getFromCode(code, buffer()->getLanguages());
 	if (lang) {
+	        // respect_os_kbd_language is not exposed yet
+	        // but just in case for now a langue change
+	        // from a keyboard switch is not a short language switch
+	        // (did not test it...)
+		if (lang != current_font.language()) {
+			current_font.fontInfo().setLangShort(FONT_OFF);
+			real_current_font.fontInfo().setLangShort(FONT_OFF);
+		}
 		current_font.setLanguage(lang);
 		real_current_font.setLanguage(lang);
 	} else
@@ -2580,9 +2588,11 @@ void Cursor::setCurrentFont()
 	setLanguageFromInput();
 
 	// special case for paragraph end
+	// but not if we are continuing a short language
 	if (cs.pos() == lastpos()
 	    && tm.isRTLBoundary(cpit, cs.pos())
-	    && !boundary()) {
+	    && !boundary()
+	    && current_font.fontInfo().langshort() != FONT_ON) {
 		Language const * lang = par.getParLanguage(bufparams);
 		current_font.setLanguage(lang);
 		current_font.fontInfo().setNumber(FONT_OFF);
diff --git a/src/Font.cpp b/src/Font.cpp
index 107ce23462..ca8ad28072 100644
--- a/src/Font.cpp
+++ b/src/Font.cpp
@@ -213,11 +213,15 @@ void Font::lyxWriteChanges(Font const & orgfont,
 	// FIXME: uncomment this when we support background.
 	//if (orgfont.fontInfo().background() != bits_.background())
 	//	os << "\\color " << lcolor.getLyXName(bits_.background()) << '\n';
-	if (orgfont.language() != language() &&
+	if ((orgfont.language() != language() ||
+	     orgfont.fontInfo().langshort() != bits_.langshort()) &&
 	    language() != latex_language) {
-		if (language())
-			os << "\\lang " << language()->lang() << "\n";
-		else
+		if (language()) {
+			os << "\\lang " << language()->lang();
+			if (bits_.langshort() == FONT_ON)
+				os << " short";
+			os << "\n";
+		} else
 			os << "\\lang unknown\n";
 	}
 
@@ -695,6 +699,7 @@ string Font::toString(bool const toggle) const
 	   << "noun " << bits_.noun() << '\n'
 	   << "number " << bits_.number() << '\n'
 	   << "nospellcheck " << bits_.nospellcheck() << '\n'
+	   << "langshort " << bits_.langshort() << '\n'
 	   << "color " << bits_.color() << '\n'
 	   << "language " << lang << '\n'
 	   << "toggleall " << convert<string>(toggle);
@@ -745,7 +750,7 @@ bool Font::fromString(string const & data, bool & toggle)
 			|| token == "noun" || token == "number"
 			|| token == "uuline" || token == "uwave"
 			|| token == "strikeout" || token == "xout"
-			|| token == "nospellcheck") {
+			|| token == "nospellcheck" || token == "langshort") {
 
 			int const next = lex.getInteger();
 			if (next == -1)
@@ -770,6 +775,8 @@ bool Font::fromString(string const & data, bool & toggle)
 				bits_.setNumber(misc);
 			else if (token == "nospellcheck")
 				bits_.setNoSpellcheck(misc);
+			else if (token == "langshort")
+				bits_.setLangShort(misc);
 
 		} else if (token == "color") {
 			int const next = lex.getInteger();
diff --git a/src/FontInfo.cpp b/src/FontInfo.cpp
index 85dace8a24..00805a917c 100644
--- a/src/FontInfo.cpp
+++ b/src/FontInfo.cpp
@@ -99,6 +99,7 @@ FontInfo const sane_font(
 	FONT_OFF,
 	FONT_OFF,
 	FONT_OFF,
+	FONT_OFF,
 	FONT_OFF);
 
 FontInfo const inherit_font(
@@ -117,6 +118,7 @@ FontInfo const inherit_font(
 	FONT_INHERIT,
 	FONT_INHERIT,
 	FONT_OFF,
+	FONT_INHERIT,
 	FONT_INHERIT);
 
 FontInfo const ignore_font(
@@ -135,6 +137,7 @@ FontInfo const ignore_font(
 	FONT_IGNORE,
 	FONT_IGNORE,
 	FONT_IGNORE,
+	FONT_IGNORE,
 	FONT_IGNORE);
 
 
@@ -264,6 +267,8 @@ void FontInfo::reduce(FontInfo const & tmplt)
 		background_ = Color_inherit;
 	if (nospellcheck_ == tmplt.nospellcheck_)
 		nospellcheck_ = FONT_INHERIT;
+	if (langshort_ == tmplt.langshort_)
+		langshort_ = FONT_INHERIT;
 }
 
 
@@ -320,6 +325,9 @@ FontInfo & FontInfo::realize(FontInfo const & tmplt)
 	if (nospellcheck_ == FONT_INHERIT)
 		nospellcheck_ = tmplt.nospellcheck_;
 
+	if (langshort_ == FONT_INHERIT)
+		langshort_ = tmplt.langshort_;
+
 	return *this;
 }
 
@@ -424,6 +432,7 @@ void FontInfo::update(FontInfo const & newfont, bool toggleall)
 	setNoun(setMisc(newfont.noun_, noun_));
 	setNumber(setMisc(newfont.number_, number_));
 	setNoSpellcheck(setMisc(newfont.nospellcheck_, nospellcheck_));
+	setLangShort(setMisc(newfont.langshort_, langshort_));
 
 	if (newfont.color_ == color_ && toggleall)
 		setColor(Color_inherit); // toggle 'back'
@@ -446,7 +455,8 @@ bool FontInfo::resolved() const
 		&& uuline_ != FONT_INHERIT && uwave_ != FONT_INHERIT
 		&& strikeout_ != FONT_INHERIT && xout_ != FONT_INHERIT
 		&& noun_ != FONT_INHERIT && color_ != Color_inherit
-		&& background_ != Color_inherit && nospellcheck_ != FONT_INHERIT);
+		&& background_ != Color_inherit && nospellcheck_ != FONT_INHERIT
+		&& langshort_ != FONT_INHERIT);
 }
 
 
diff --git a/src/FontInfo.h b/src/FontInfo.h
index eb69fd3386..169354b79b 100644
--- a/src/FontInfo.h
+++ b/src/FontInfo.h
@@ -50,11 +50,13 @@ public:
 		FontState  uwave,
 		FontState  noun,
 		FontState  number,
-		FontState  nospellcheck)
+		FontState  nospellcheck,
+		FontState  langshort)
 		: family_(family), series_(series), shape_(shape), size_(size),
 		style_(style), color_(color), background_(background), emph_(emph),
 		underbar_(underbar), strikeout_(strikeout), xout_(xout), uuline_(uuline),
-		uwave_(uwave), noun_(noun), number_(number), nospellcheck_(nospellcheck)
+		uwave_(uwave), noun_(noun), number_(number), nospellcheck_(nospellcheck),
+		langshort_(langshort)
 	{}
 
 	/// Decreases font size by one
@@ -96,6 +98,8 @@ public:
 	void setBackground(ColorCode b) { background_ = b; }
 	FontState nospellcheck() const { return nospellcheck_; }
 	void setNoSpellcheck(FontState n) { nospellcheck_ = n; }
+	FontState langshort() const { return langshort_; }
+	void setLangShort(FontState s) { langshort_ = s; }
 	//@}
 
 	///
@@ -202,6 +206,8 @@ private:
 	FontState number_;
 	///
 	FontState nospellcheck_;
+	///
+	FontState langshort_;
 };
 
 
@@ -222,7 +228,8 @@ inline bool operator==(FontInfo const & lhs, FontInfo const & rhs)
 		&& lhs.uwave_ == rhs.uwave_
 		&& lhs.noun_ == rhs.noun_
 		&& lhs.number_ == rhs.number_
-		&& lhs.nospellcheck_ == rhs.nospellcheck_;
+		&& lhs.nospellcheck_ == rhs.nospellcheck_
+		&& lhs.langshort_ == rhs.langshort_;
 }
 
 
diff --git a/src/LyXAction.cpp b/src/LyXAction.cpp
index 390312bfa8..e3a10056c0 100644
--- a/src/LyXAction.cpp
+++ b/src/LyXAction.cpp
@@ -2498,7 +2498,7 @@ void LyXAction::init()
 /*!
  * \var lyx::FuncCode lyx::LFUN_LANGUAGE
  * \li Action: Set language from the current cursor position.
- * \li Syntax: language <LANG> [set]
+ * \li Syntax: language <LANG> [set] [short]
  * \li Params: <LANG>: Requested language. Look in lib/languages for
                        the list. "language reset" or "language" (without param)
                        reset to the document language.
@@ -2506,6 +2506,9 @@ void LyXAction::init()
                        language. Otherwise, the language will be toggled (i.e., if
                        the current language is LANG, switch to the document language
                        or the default language, if LANG is the document language).
+                short: If used, the language change is forced to be
+                       a short switch (\foreignlanguage etc.)
+                       even if it covers the paragraph's first position.
  * \li Origin: Dekel, 2 Mar 2000
  * \endvar
  */
diff --git a/src/Paragraph.cpp b/src/Paragraph.cpp
index b75d8ecaf6..d101a0788d 100644
--- a/src/Paragraph.cpp
+++ b/src/Paragraph.cpp
@@ -4401,8 +4401,13 @@ bool Paragraph::isSpace(pos_type pos) const
 Language const *
 Paragraph::getParLanguage(BufferParams const & bparams) const
 {
-	if (!empty())
-		return getFirstFontSettings(bparams).language();
+	if (!empty()) {
+		// Skip leading runs that of short language switch
+		for (auto const & f : d->fontlist_)
+			if (f.font().fontInfo().langshort() != FONT_ON)
+				return f.font().language();
+		return bparams.language;
+	}
 	// FIXME: we should check the prev par as well (Lgb)
 	return bparams.language;
 }
diff --git a/src/Text.cpp b/src/Text.cpp
index e41b86232b..8888f7bbf9 100644
--- a/src/Text.cpp
+++ b/src/Text.cpp
@@ -496,15 +496,27 @@ void Text::readParToken(Paragraph & par, Lexer & lex,
 		lex.next();
 		setLyXSize(lex.getString(), font.fontInfo());
 	} else if (token == "\\lang") {
-		lex.next();
-		string const tok = lex.getString();
-		Language const * lang = languages.getLanguage(tok);
+		lex.eatLine();
+		string const rest = lex.getString(true);
+		string langname = rest;
+		FontState langshort = FONT_OFF;
+		size_t const sp = rest.find(' ');
+		if (sp != string::npos) {
+			langname = rest.substr(0, sp);
+			string const flag = trim(rest.substr(sp + 1), " \t");
+			if (flag == "short")
+				langshort = FONT_ON;
+			else if (!flag.empty())
+				lex.printError("Unknown \\lang flag `" + flag + "'");
+		}
+		Language const * lang = languages.getLanguage(langname);
 		if (lang) {
 			font.setLanguage(lang);
 		} else {
 			font.setLanguage(bp.language);
 			lex.printError("Unknown language `$$Token'");
 		}
+		font.fontInfo().setLangShort(langshort);
 	} else if (token == "\\numeric") {
 		lex.next();
 		font.fontInfo().setNumber(setLyXMisc(lex.getString()));
@@ -2784,6 +2796,8 @@ void Text::setFont(Cursor & cur, Font const & font, bool toggleall)
 			newfi.setNumber(oldfi.number() == FONT_OFF ? FONT_ON : FONT_OFF);
 		if (newfi.nospellcheck() == FONT_TOGGLE)
 			newfi.setNoSpellcheck(oldfi.nospellcheck() == FONT_OFF ? FONT_ON : FONT_OFF);
+		if (newfi.langshort() == FONT_TOGGLE)
+			newfi.setLangShort(oldfi.langshort() == FONT_OFF ? FONT_ON : FONT_OFF);
 	}
 
 	setFont(cur.bv(), cur.selectionBegin().top(),
@@ -5951,9 +5965,20 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
 		// was requested via empty or "reset" arg.
 		if (!lang && !reset)
 			break;
-		bool const toggle = (cmd.getArg(1) != "set");
+		// Remaining args ("set", "short") may appear in any order.
+		bool toggle = true;
+		bool short_lang = false;
+		for (int i = 1; !cmd.getArg(i).empty(); ++i) {
+			string const arg = cmd.getArg(i);
+			if (arg == "set")
+				toggle = false;
+			else if (arg == "short")
+				short_lang = true;
+		}
 		selectWordWhenUnderCursor(cur, WHOLE_WORD_STRICT);
 		Font font(ignore_font, lang);
+		if (short_lang)
+			font.fontInfo().setLangShort(FONT_TOGGLE);
 		toggleAndShow(cur, this, font, toggle);
 		break;
 	}
diff --git a/src/frontends/qt/GuiCharacter.cpp b/src/frontends/qt/GuiCharacter.cpp
index 7995c28fcc..976cc04377 100644
--- a/src/frontends/qt/GuiCharacter.cpp
+++ b/src/frontends/qt/GuiCharacter.cpp
@@ -156,7 +156,7 @@ void fillCombo(QComboBox * combo, QList<T> const & list)
 GuiCharacter::GuiCharacter(GuiView & lv)
 	: GuiDialog(lv, "character", qt_("Text Properties")),
 	  font_(ignore_font, ignore_language),
-	  emph_(false), noun_(false), nospellcheck_(false)
+	  emph_(false), noun_(false), nospellcheck_(false), langshort_(false)
 {
 	setupUi(this);
 
@@ -213,6 +213,7 @@ GuiCharacter::GuiCharacter(GuiView & lv)
 	bc().addReadOnly(nounCB);
 	bc().addReadOnly(emphCB);
 	bc().addReadOnly(nospellcheckCB);
+	bc().addReadOnly(langshortCB);
 	bc().addReadOnly(langCO);
 	bc().addReadOnly(colorCO);
 	bc().addReadOnly(autoapplyCB);
@@ -271,6 +272,17 @@ void GuiCharacter::on_nospellcheckCB_clicked()
 }
 
 
+void GuiCharacter::on_langshortCB_clicked()
+{
+	// skip intermediate state at user click
+	if (!langshort_) {
+		langshortCB->setCheckState(Qt::Checked);
+		langshort_ = true;
+	}
+	change_adaptor();
+}
+
+
 void GuiCharacter::resetToDefault()
 {
 	Font font(inherit_font);
@@ -406,6 +418,7 @@ void GuiCharacter::checkRestoreDefaults()
 		|| setMarkupState(emphCB->checkState()) != FONT_OFF
 		|| setMarkupState(nounCB->checkState()) != FONT_OFF
 		|| setMarkupState(nospellcheckCB->checkState()) != FONT_OFF
+		|| setMarkupState(langshortCB->checkState()) != FONT_OFF
 		|| bar[ulineCO->currentIndex()].second != INHERIT
 		|| strike[strikeCO->currentIndex()].second != INHERIT
 		|| lcolor.getFromLyXName(fromqstr(colorCO->itemData(colorCO->currentIndex()).toString()), false) != Color_inherit
@@ -419,6 +432,7 @@ void GuiCharacter::checkRestoreDefaults()
 		|| setMarkupState(emphCB->checkState()) != FONT_IGNORE
 		|| setMarkupState(nounCB->checkState()) != FONT_IGNORE
 		|| setMarkupState(nospellcheckCB->checkState()) != FONT_IGNORE
+		|| setMarkupState(langshortCB->checkState()) != FONT_IGNORE
 		|| bar[ulineCO->currentIndex()].second != IGNORE
 		|| strike[strikeCO->currentIndex()].second != IGNORE
 		|| lcolor.getFromLyXName(fromqstr(colorCO->itemData(colorCO->currentIndex()).toString()), false) != Color_ignore
@@ -460,6 +474,8 @@ void GuiCharacter::updateContents()
 				font.fontInfo().setNoun(FONT_IGNORE);
 			if (fi.nospellcheck() != tmp.fontInfo().nospellcheck())
 				font.fontInfo().setNoSpellcheck(FONT_IGNORE);
+			if (fi.langshort() != tmp.fontInfo().langshort())
+				font.fontInfo().setLangShort(FONT_IGNORE);
 			if (fi.color() != tmp.fontInfo().color())
 				font.fontInfo().setColor(Color_ignore);
 			if (fi.underbar() != tmp.fontInfo().underbar()
@@ -568,9 +584,11 @@ void GuiCharacter::paramsToDialog(Font const & font)
 	emphCB->setCheckState(getMarkupState(fi.emph()));
 	nounCB->setCheckState(getMarkupState(fi.noun()));
 	nospellcheckCB->setCheckState(getMarkupState(fi.nospellcheck()));
+	langshortCB->setCheckState(getMarkupState(fi.langshort()));
 	emph_ = emphCB->checkState() == Qt::Checked;
 	noun_ = nounCB->checkState() == Qt::Checked;
 	nospellcheck_ = nospellcheckCB->checkState() == Qt::Checked;
+	langshort_ = langshortCB->checkState() == Qt::Checked;
 
 	// reset_language is a null pointer.
 	QString const lang = (font.language() == reset_language)
@@ -589,6 +607,7 @@ void GuiCharacter::applyView()
 	fi.setEmph(setMarkupState(emphCB->checkState()));
 	fi.setNoun(setMarkupState(nounCB->checkState()));
 	fi.setNoSpellcheck(setMarkupState(nospellcheckCB->checkState()));
+	fi.setLangShort(setMarkupState(langshortCB->checkState()));
 	setBar(fi, bar[ulineCO->currentIndex()].second);
 	setStrike(fi, strike[strikeCO->currentIndex()].second);
 	setLyXColor(fromqstr(colorCO->getData(colorCO->currentIndex())), fi);
diff --git a/src/frontends/qt/GuiCharacter.h b/src/frontends/qt/GuiCharacter.h
index d3345c210f..4d01cc96a3 100644
--- a/src/frontends/qt/GuiCharacter.h
+++ b/src/frontends/qt/GuiCharacter.h
@@ -68,6 +68,7 @@ protected Q_SLOTS:
 	void on_emphCB_clicked();
 	void on_nounCB_clicked();
 	void on_nospellcheckCB_clicked();
+	void on_langshortCB_clicked();
 	void resetToDefault();
 	void resetToNoChange();
 	void checkRestoreDefaults();
@@ -109,6 +110,8 @@ private:
 	bool noun_;
 	///
 	bool nospellcheck_;
+	///
+	bool langshort_;
 
 	///
 	QAction * resetdefault_ = new QAction(this);
diff --git a/src/frontends/qt/ui/CharacterUi.ui b/src/frontends/qt/ui/CharacterUi.ui
index 97d7f5caef..035ff1b4a8 100644
--- a/src/frontends/qt/ui/CharacterUi.ui
+++ b/src/frontends/qt/ui/CharacterUi.ui
@@ -291,6 +291,16 @@
         </property>
        </widget>
       </item>
+      <item row="2" column="0">
+       <widget class="QCheckBox" name="langshortCB">
+        <property name="toolTip">
+         <string>If this is selected, the marked text is always output as short language switch (e.g. \foreignlanguage)</string>
+        </property>
+        <property name="text">
+         <string>&amp;Short Language Switch</string>
+        </property>
+       </widget>
+      </item>
      </layout>
     </widget>
    </item>
diff --git a/src/version.h b/src/version.h
index 1c0ba33986..a0df812cd8 100644
--- a/src/version.h
+++ b/src/version.h
@@ -32,8 +32,8 @@ extern char const * const lyx_version_info;
 
 // Do not remove the comment below, so we get merge conflict in
 // independent branches. Instead add your own.
-#define LYX_FORMAT_LYX 650 // spitz: language package polyglossia
-#define LYX_FORMAT_TEX2LYX 650
+#define LYX_FORMAT_LYX 651 // ufogiel: forced short (local) language switch
+#define LYX_FORMAT_TEX2LYX 651
 
 #if LYX_FORMAT_TEX2LYX != LYX_FORMAT_LYX
 #ifndef _MSC_VER
-- 
lyx-cvs mailing list
[email protected]
https://lists.lyx.org/mailman/listinfo/lyx-cvs