\NeedsTeXFormat{LaTeX2e} \ProvidesExplPackage {polyglossia} {2024/07/15} {v2.2} {Modern multilingual typesetting with XeLaTeX and LuaLaTeX} % TODO Handle remaining uses in the gloss files (\patchcmd) % and remove package call afterwards \RequirePackage{etoolbox} % Will raise error if used with anything else than XeTeX or LuaTeX \RequirePackage{fontspec}[2010/06/08]% v2.0 \RequirePackage{iftex} \prg_generate_conditional_variant:Nnn \clist_if_in:Nn {Ne} {TF, T, F} % check if needed %% The following is for compatibility with Babel-aware package: % \languageshorthands is provided by babelsh.def, which is % only loaded by some glosses, but some classes presuppose % it is there generally. So let's provide a stub: \ProvideDocumentCommand \languageshorthands { m } {} % These have to be provided at the end of the preamble \hook_gput_code:nnn {begindocument/before} {.} { \cs_gset_eq:NN \bbl@set@language \xpg_set_language_aux:nn % for biblatex \cs_gset_eq:NN \bbl@main@language \xpg_main_language_tl % for biblatex \ProvideDocumentCommand \texorpdfstring { m m } { #1 } % dummy command if hyperref is not loaded } %% when no patterns are available, we use \l@nohyphenation, assigned to 255 %% (suggestion by Enrico Gregorio) %% \l@nohyphenation is defined in polyglossia.lua \sys_if_engine_luatex:TF { \lua_load_module:n{polyglossia} }{ \cs_if_free:cT { l@nohyphenation } { \chardef\l@nohyphenation=255 } } % Which version of XeTeX do we use? What is the boundary class? 4095 or 255 \cs_if_exist:cTF { e@alloc@intercharclass@top } { \cs_gset_eq:NN \xpg@boundaryclass \e@alloc@intercharclass@top } { \chardef\xpg@boundaryclass=\@cclv } % Useful for getting list of loaded languages and variants. Like babel's bbl@loaded % all language loaded \seq_new:N \__xpg_langs_loaded % list of loaded languages (polyglossia name) \clist_new:N \xpg@loaded % list of loaded variants \clist_new:N \xpg@vloaded % list of loaded languages (babel name) \clist_new:N \xpg@bloaded % list of loaded languages (bcp-47 id) \clist_new:N \xpg@bcp@loaded % output counter as lower-case latin letter \DeclareExpandableDocumentCommand \latinalph { m } { \exp_args:Nc \latin@alph {c@#1} } % output counter as upper-case latin letter \DeclareExpandableDocumentCommand \latinAlph { m } { \exp_args:Nc \latin@Alph {c@#1} } %% Internal hooks % select language hook \cs_new_nopar:Nn \__xpg_at_begin_document_selectlanguage: {} % \disablehyphenation hook \cs_new_nopar:Nn \__xpg_at_begin_document_hyphenation: {} % hook to be executed at begin of document \cs_new_nopar:Nn \__xpg_at_begin_document: { % save various command \cs_gset_eq:cc{latin@alph}{@alph}% TODO rename when we have the C locale \cs_gset_eq:cc{latin@Alph}{@Alph}% TODO rename when we have the C locale % push to C language gloss \cs_gset_eq:cc{xpg_Clang_arabic}{@arabic} \xpg_initial_setup: % apply \familydefault changes \xpg_set_familydefault: } \hook_gput_code:nnn {begindocument} {.} { \__xpg_at_begin_document: } % The following needs to go after any \AtBeginDocument (also of packages % loaded after \set[main|other]language % Track whether the main language has been set \bool_new:N \g_xpg_main_lang_set_bool \hook_gput_code:nnn {begindocument/end} {.} { % now we have the C locale definition: select the language \__xpg_at_begin_document_selectlanguage: \bool_gset_true:N \g_xpg_main_lang_set_bool % If hyphenation disabling has been requested in preamble, do it now \__xpg_at_begin_document_hyphenation: } % % MESSAGES % % message templates \msg_new:nnn { polyglossia } { general } { #1 } \msg_new:nnn { polyglossia } { languagenotloaded } { The~ language~ #1~ is~ not~ loaded.~ You~ must~ load~ it~ in~ order~ to~ use~ it. } \msg_redirect_name:nnn { polyglossia } { languagenotloaded } { critical } \msg_new:nnn { polyglossia } { languagenolongerloaded } { The~ language~ #1~ is~ no~ longer~ loaded.~ Please~ rerun~ LaTeX. } \msg_redirect_name:nnn { polyglossia } { languagenolongerloaded } { warning } \msg_new:nnn { polyglossia } { unknownlocalnumeral } { Unknown~ key~ "#1"~ in~ \string\localnumeral. } \msg_new:nnn { polyglossia } { localnumeralemptyvalue } { Keys~ of~ \string\localnumeral~ must~ have~ a~ value. } \msg_new:nnn { polyglossia } { illvalue } { Illegal~ value~ (#1)~ for~ #2! } \msg_new:nnn { polyglossia } { illarg } { Invalid~ argument~ (#1)~ for~ #2! } \msg_new:nnn { polyglossia } { nopatterns } { No~ hyphenation~ patterns~ were~ loaded~ for~ `#2' \iow_newline: I~ will~ use~ \string\language=\string\l@ #1\space instead. } \msg_new:nnn { polyglossia } { undefcmd } { \tl_to_str:N {#1} ~ is~ not~ defined! } %% custom message macros \cs_new_nopar:Nn \xpg_error_msg:n { \exp_args:Nnne \msg_error:nnn { polyglossia } { general } { #1 } } \cs_new_nopar:Nn \xpg_warning_msg:n { \exp_args:Nnne \msg_warning:nnn { polyglossia } { general } { #1 } } \cs_new_nopar:Nn \xpg_info_msg:n { \exp_args:Nnne \msg_info:nnn { polyglossia } { general } { #1 } } \cs_new_nopar:Nn \xpg_no_patterns_msg:n { \msg_warning:nnnn { polyglossia } { nopatterns } { nohyphenation } { #1 } } \cs_new_nopar:Nn \xpg_ill_value_msg:nn { \msg_warning:nnnn { polyglossia } { illvalue } { #1 } { #2 } } \cs_new_nopar:Nn \xpg_ill_arg_msg:nn { \msg_error:nnnn { polyglossia } { illarg } { #1 } { #2 } } % error out if lang is not loaded \cs_new_nopar:Nn \xpg_error_if_lang_not_loaded:n { \seq_if_in:NeF \__xpg_langs_loaded {#1} { \msg_error:nnn { polyglossia } { languagenotloaded } { #1 } } } %% use macro if defined, else warn that it is not \cs_new_nopar:Nn \__xpg_use_or_warn:N { \cs_if_exist_use:NF {#1} { \msg_error:nnn { polyglossia } { undefcmd } { #1 } } } \cs_generate_variant:Nn \__xpg_use_or_warn:N {c} % gloss message interface \cs_set_eq:cc { xpg@error } { xpg_error_msg:n } \cs_set_eq:cc { xpg@warning } { xpg_warning_msg:n } \cs_set_eq:cc { xpg@info } { xpg_info_msg:n } \cs_set_eq:cc { xpg@ill@value } { xpg_ill_value_msg:nn } \NewDocumentCommand \XPGNoPatternsFallback { O{ nohyphenation } m } { \msg_warning:nnnn { polyglossia } { nopatterns } { #1 } { #2 } \exp_args:Ncc \adddialect {l@#2} {l@#1} } \NewDocumentCommand \CheckHyphenationPatterns { m } { \xpg_if_language_defined:nF {#1} { \XPGNoPatternsFallback{#1} } } % % END MESSAGES %% ensure directionality if bidi is loaded, else ignore %%% FIXME still used? \cs_new_nopar:Npn \@@ensure@dir #1 { \cs_if_exist_use:c{@ensure@dir}{#1} } \cs_new_nopar:Npn \@@ensure@maindir #1 { \cs_if_exist_use:c{@ensure@maindir}{#1} } % if we are in the document preamble run T else F \prg_set_conditional:Nnn \xpg_if_in_preamble: {T, F, TF} { \cs_if_eq:NNTF { \@onlypreamble } { \@notprerr } { \prg_return_false: } { \prg_return_true: } } %% Used by the language definitions files for right-to-left languages \DeclareDocumentCommand \RequireBidi {} { \xpg_if_in_preamble:T { \sys_if_engine_luatex:TF { \RequirePackage{luabidi} } { \RequirePackage{bidi} } } \DeclareDocumentCommand \RequireBidi {} {} } % if #1 is LR run T else F \prg_set_conditional:Nnn \__xpg_if_LR_str:n {p, T, F, TF} { \str_case_e:nnF{#1} { {LR}{\prg_return_true:} {RL}{\prg_return_false:} } { \xpg_error_msg:n {Unknown~ direction~#1} \prg_return_false: } } \prg_generate_conditional_variant:Nnn \__xpg_if_LR_str:n {e} {p, T, F, TF} % (lua)bidi commands to change directionality for paragraphs % and inline text. % overwritten with correct package \cs_new_nopar:Nn \__xpg_set_par_direction:n { \__xpg_if_LR_str:nF {#1} { \xpg_error_msg:n {right-to-left,~ but~ (lua)bidi~ package~ was~ not~ loaded!} } } \cs_new_nopar:Nn \__xpg_set_text_direction:n { \__xpg_if_LR_str:nF {#1} { \xpg_error_msg:n {right-to-left,~ but~ (lua)bidi~ package~ was~ not~ loaded!} } } \hook_gput_code:nnn {package/bidi/after} {.} { \cs_gset_nopar:Nn \__xpg_set_par_direction:n { \__xpg_if_LR_str:nTF{#1} { \setLR } { \setRL } } \cs_gset_nopar:Nn \__xpg_set_text_direction:n { \__xpg_if_LR_str:nTF{#1} { \LRE } { \RLE } } } \hook_gput_code:nnn {package/luabidi/after} {.} { \cs_gset_nopar:Nn \__xpg_set_par_direction:n { \__xpg_if_LR_str:nTF{#1} { \setLR } { \setRL } } \cs_gset_nopar:Nn \__xpg_set_text_direction:n { \__xpg_if_LR_str:nTF{#1} { \LRE } { \RLE } } } % emulate \RTLmain \sys_if_engine_luatex:TF { \cs_new_nopar:Nn \__xpg_setRTLmain: { \setRTLmain } } { \cs_new_nopar:Nn \__xpg_setRTLmain: { \@RTLmaintrue\setnonlatin } } %% compatibility with babel \cs_set:Npn \addto #1 #2 { \cs_if_exist:NF { #1 } { \cs_new:Npn { #1 } {} } \tl_gput_right:Nn { #1 } { #2 } } %% SETUP INTERFACE FOR GLOSS FILES %% options currently available: %% language : the name of the language (as understood by fontspec) %% hyphennames : the different hyphenation patterns to try (comma separated list) %%% TODO: if pattern is prefixed by !, then it should be loaded as a fallback, %%%% with \CheckHyphenationPatterns - i.e. with a warning: e.g. sanskrit for hindi, %%%% or catalan for asturian. – Also for languages with variants! %%%% (English and German, etc.) %% script : the name of the script (as understood by fontspec) – default is Latin %% scripttag : the OpenType tag for the script %% langtag : the OpenType tag for the language %% hyphenmins : the hyphenmins for this language (comma-sep list of two integers) %% frenchspacing : boolean %% indentfirst : boolean %% fontsetup : boolean %% TODO: nouppercase : boolean (for scripts like Arabic, Devanagari, etc which have %% no concept of uppercase/lowercase) %% TODO: localalph = {,} %% TODO: localnumeral = %% or even better localdigits = {0123456789} for fully automatic setup \NewDocumentCommand \PolyglossiaSetup { m m } { \__xpg_keys_define_lang:n{#1} \keys_set:nn { polyglossia / #1 } { #2 } \__xpg_setup_hyphen:n {#1} %define booleans etoolbox style and set defaults %% TODO ? \providetoggle{#1@setup@done}% % we initialize this so that we can append below \cs_gset:cpn {init@extras@#1} {} % here we do the fontsetup: \__xpg_auto_setupfont:n { #1 } %% TODO? \toggletrue{#1@setup@done} % register base alias \xpg_language_alias { #1 } { #1 } } % Adjust language key setting after initial setup. % Principally any key can be altered this way. % The command is mainly used in gloss file where % different options (variant, script, etc.) result % in different babel names, bcp47 specification, % or OpenType language or script tags. \DeclareDocumentCommand \SetLanguageKeys { m m } { \clist_map_inline:nn { #1 } { \keys_set:nn { polyglossia / ##1 } { #2 } } } \bool_new:N \l__xpg_have_hyphen_bool % setup hyphennames from a str list of hyphen \cs_new:Nn \__xpg_setup_hyphen:n { \clist_set:Ne{\l_tmpa_clist}{\prop_item:Nn \l_xpg_langsetup_prop {#1 / hyphennames}} \bool_set_false:N \l__xpg_have_hyphen_bool % for each hyphen in the set until we find one that works \clist_map_inline:Nn \l_tmpa_clist { \bool_if:NF \l__xpg_have_hyphen_bool { % check if language hyphenname is defined \__xpg_pattern_check_if_exists:nF{#1} { % if not, first consider nohyphenation \str_if_eq:nnTF{##1}{nohyphenation} { \cs_gset_eq:cc{l@#1}{l@##1} \bool_gset_true:N \l__xpg_have_hyphen_bool } { % then test if hyphenation is defined \xpg_if_language_defined:nT {##1} { % test if language hyphenation is nohyphenation \cs_if_eq:cNTF{l@#1}{\l@nohyphenation} { \bool_gset_true:N \l__xpg_have_hyphen_bool } { % if false define language to hyphenation if it is not equal... \str_if_eq:nnF{#1}{##1}{\cs_gset_eq:cc{l@#1}{l@##1}} % ...and load \xpg_set_hyphenation_patterns:n {##1} \bool_gset_true:N \l__xpg_have_hyphen_bool } } } } } } % if l@#1 does not yet exist, % we assign it to nohyphenation % we do this here in case and if the hyphennames key was omitted \bool_if:NF \l__xpg_have_hyphen_bool { \CheckHyphenationPatterns{#1} } \cs_gset:cpn {#1@language} { \SetupPolyglossiaLangPatterns{#1} } % setup hyphenmins \clist_set:Ne \l_tmpa_clist { \prop_item:Nn \l_xpg_langsetup_prop {#1 / hyphenmins} } \cs_if_eq:cNF {l@#1} \l@nohyphenation { \use:x { \exp_not:N \setlocalhyphenmins {#1} { \clist_item:Nn \l_tmpa_clist {1} } { \clist_item:Nn \l_tmpa_clist {2} } } } } \NewDocumentCommand \SetupPolyglossiaLangPatterns { m } { \bool_if:NTF \g__xpg_hyphenation_disabled_bool { \tl_gset:Ne \g__xpg_lastlanguage_tl {\the\csname l@#1\endcsname} }{ % first, test if \l@#1 exists % without that, \csname l@#1\endcsname will be defined as \relax \cs_if_exist:cTF {l@#1} { \cs_if_eq:cNTF {l@#1} \l@nohyphenation { \language=\l@nohyphenation } { \xpg_set_hyphenation_patterns:n {#1} } } { % Since this function is sometimes called from the gloss files % directly, we need to check whether the requested hyphenname exists. \CheckHyphenationPatterns{#1} \xpg_set_hyphenation_patterns:n {#1} } } } \prop_new_linked:N \l_xpg_langsetup_prop \cs_new_protected:Npn \__xpg_keys_define_lang:n #1 { \keys_define:nn {polyglossia} { % the script font #1 / script .code:n = { \prop_put:Nnn \l_xpg_langsetup_prop {#1/script}{##1} \prop_put:Nne \l_xpg_langsetup_prop {#1/lcscript} {\tl_if_empty:nF{##1}{\str_lowercase:n{##1}}} }, #1 / script .value_required:n = true, #1 / script .initial:n = latin, % the opentype script tag #1 / scripttag .code:n = {\prop_put:Nnn \l_xpg_langsetup_prop {#1/scripttag}{##1}}, #1 / scripttag .default:n = {}, #1 / scripttag .initial:n = {}, % the language full name #1 / language .code:n = {\prop_put:Nnn \l_xpg_langsetup_prop {#1/language}{##1}}, #1 / language .value_required:n = true, #1 / language .initial:x = {\str_uppercase:n#1}, % the language tag #1 / langtag .code:n = {\prop_put:Nnn \l_xpg_langsetup_prop {#1/langtag}{##1}}, #1 / langtag .value_required:n = true, #1 / langtag .initial:n = {}, % the BCP-47 tag #1 / bcp47 .code:n = {\prop_put:Nnn \l_xpg_langsetup_prop {#1/bcp47}{##1}}, #1 / bcp47 .value_required:n = true, #1 / bcp47 .initial:n = {}, % the BCP-47 language tag #1 / bcp47-language .code:n = {\prop_put:Nnn \l_xpg_langsetup_prop {#1/bcp47-language}{##1}}, #1 / bcp47-language .value_required:n = true, #1 / bcp47-language .initial:n = {}, % the BCP-47 region tag #1 / bcp47-region .code:n = {\prop_put:Nnn \l_xpg_langsetup_prop {#1/bcp47-region}{##1}}, #1 / bcp47-region .value_required:n = false, #1 / bcp47-region .initial:n = {}, % the BCP-47 script tag #1 / bcp47-script .code:n = { \prop_put:Nnn \l_xpg_langsetup_prop {#1/bcp47-script}{##1} \prop_put:Nne \l_xpg_langsetup_prop {#1/lc-bcp47-script} {\tl_if_empty:nF{##1}{\str_lowercase:n{##1}}} }, #1 / bcp47-script .value_required:n = true, #1 / bcp47-script .initial:n = {Latn}, % the BCP-47 variant tag #1 / bcp47-variant .code:n = {\prop_put:Nnn \l_xpg_langsetup_prop {#1/bcp47-variant}{##1}}, #1 / bcp47-variant .value_required:n = false, #1 / bcp47-variant .initial:n = {}, % the BCP-47 extension-t tag #1 / bcp47-extension-t .code:n = {\prop_put:Nnn \l_xpg_langsetup_prop {#1/bcp47-extension-t}{##1}}, #1 / bcp47-extension-t .value_required:n = false, #1 / bcp47-extension-t .initial:n = {}, % the BCP-47 extension-u tag #1 / bcp47-extension-u .code:n = {\prop_put:Nnn \l_xpg_langsetup_prop {#1/bcp47-extension-u}{##1}}, #1 / bcp47-extension-u .value_required:n = false, #1 / bcp47-extension-u .initial:n = {}, % the BCP-47 extension-x tag #1 / bcp47-extension-x .code:n = {\prop_put:Nnn \l_xpg_langsetup_prop {#1/bcp47-extension-x}{##1}}, #1 / bcp47-extension-x .value_required:n = false, #1 / bcp47-extension-x .initial:n = {}, % the BCP-47 casing alias #1 / bcp47-casing .code:n = {\prop_put:Nnn \l_xpg_langsetup_prop {#1/bcp47-casing}{##1}}, #1 / bcp47-casing .value_required:n = false, #1 / bcp47-casing .initial:n = {}, % hyphennames #1 / hyphennames .code:n = { \clist_set:Nn{\l_tmpa_clist}{##1} \prop_put:Nne \l_xpg_langsetup_prop {#1/hyphennames}{\clist_use:Nn \l_tmpa_clist {,}} }, #1 / hyphennames .value_required:n = true, #1 / hyphennames .initial:x = {\c_empty_clist}, % direction #1 / direction . code:n = { \str_case_e:nnTF{##1}{ {LR}{} {RL}{\RequireBidi} } { \prop_put:Nnn \l_xpg_langsetup_prop {#1/direction}{##1} } { \xpg_error_msg:n {Unknown~ direction~ "##1"~ for~ language~ "#1"} } }, #1 / direction .value_required:n = true, #1 / direction .initial:n = {LR}, % minimal left and right hyphenation minima using #1 / hyphenmins .code:n = { % check syntax \int_compare:nNnF { \clist_count:n {##1} } = {2} { \xpg_error_msg:n {hypenmins~should~be~a~list~of~two~entries,~got~"##1"} } % set prop \prop_put:Nnn \l_xpg_langsetup_prop {#1/hyphenmins} {##1} }, #1 / hyphenmins .value_required:n = true, #1 / hyphenmins .initial:n = {2,3}, % minimal length for hyphenation (LuaTeX only) #1 / totalhyphenmin .code:n = { % check syntax \int_compare:nNnF { \clist_count:n {##1} } = {1} { \xpg_error_msg:n {totalhyphenhypenmin~should~be~a~single~entry,~got~"##1"} } % set prop \prop_put:Nnn \l_xpg_langsetup_prop {#1/totalhyphenmin} {##1} }, #1 / totalhyphenmin .value_required:n = false, % frenchspacing #1 / frenchspacing .bool_gset:c = g__xpg_#1_fs_bool , #1 / frenchspacing .default:n = true , #1 / frenchspacing .initial:n = false , % indent first line #1 / indentfirst .bool_gset:c = g__xpg_#1_if_bool , #1 / indentfirst .default:n = true , #1 / indentfirst .initial:n = false , % fontsetup #1 / fontsetup .bool_gset:c = g__xpg_#1_fontsetup_bool , #1 / fontsetup .default:n = true , #1 / fontsetup .initial:n = false , % environment name #1 / envname .code:n = { \prop_put:Nnn \l_xpg_langsetup_prop {#1/envname}{##1} }, #1/ envname.value_required:n = true, #1/ envname.initial:n = {#1}, % babel name #1 / babelname .code:n = { \prop_put:Nnn \l_xpg_langsetup_prop {#1/babelname}{##1} }, #1/ babelname.value_required:n = true, #1/ babelname.initial:n = {#1}, % default numerals #1 / localnumeral . code:n = { \prop_put:Nnn \l_xpg_langsetup_prop {#1/localnumeral}{##1} \prop_put:Nnn \l_xpg_langsetup_prop {#1/Localnumeral}{##1} }, #1 / localnumeral.value_required:n = true, #1 / localnumeral.initial:n = {xpg_C_localnumeral:nn}, % uppercased #1 / Localnumeral . code:n = { \prop_put:Nnn \l_xpg_langsetup_prop {#1/Localnumeral}{##1} }, #1 / Localnumeral.value_required:n = true, #1 / Localnumeral.initial:n = {xpg_C_localnumeral:nn}, % environment define command (by default create the environment) #1 / DefineCommandsCmd .code:n = {\prop_put:Nnn \l_xpg_langsetup_prop {#1/DefineCommandsCmd}{##1}}, #1 / DefineCommandsCmd .value_required:n = true, #1 / DefineCommandsCmd .initial:n = {xpg_define_language_commands:e} } } \DeclareExpandableDocumentCommand \babelname { } { \prop_item:Ne \l_xpg_langsetup_prop { \languagename / babelname } } \DeclareExpandableDocumentCommand \mainbabelname { } { \prop_item:Ne \l_xpg_langsetup_prop { \mainlanguagename / babelname } } % TODO move to C module \cs_new:Nn \xpg_C_localnumeral:nn { \xpg_Clang_arabic{#2} } \cs_new:Npn \__xpg_localnumeral_parse:nn #1 #2 { \str_if_eq:eeF { #1 } { lang } { \msg_error:nnn { polyglossia } { unknownlocalnumeral } { #1 } } \exp_args:Ne \str_case:nnF { #2 } { { local } { } { main } { \foreignlanguage { \mainlanguagename } } } { \foreignlanguage { #2 } } } \cs_new:Npn \__xpg_localnumeral:nnn #1 #2 #3 { \use:e { \keyval_parse:nnn { \msg_error:nnn { polyglossia } { localnumeralemptyvalue } } { \__xpg_localnumeral_parse:nn } { #2 } } { \use:c { \prop_item:Ne \l_xpg_langsetup_prop { \languagename / #3 } } { } { #1 } } } \NewExpandableDocumentCommand \localnumeral { s O{ lang=local } m } { \IfBooleanTF { #1 } { \exp_args:Nc \__xpg_localnumeral:nnn { c@#3 } { #2 } { localnumeral } } { \__xpg_localnumeral:nnn { #3 } { #2 } { localnumeral } } } \NewExpandableDocumentCommand \Localnumeral { s O{ lang=local } m } { \IfBooleanTF { #1 } { \exp_args:Nc \__xpg_localnumeral:nnn { c@#3 } { #2 } { Localnumeral } } { \__xpg_localnumeral:nnn { #3 } { #2 } { Localnumeral } } } \cs_new_nopar:Npn \__xpg_french_spacing:n #1 { \bool_if:cTF { g__xpg_#1_fs_bool } { \frenchspacing } { \nonfrenchspacing } } \cs_new_nopar:Npn \__xpg_indent_first:n #1 { \bool_if:cTF { g__xpg_#1_if_bool } { \__xpg_french_indent: } { \__xpg_no_french_indent: } } \cs_new:Nn \__xpg_lang_set_par_direction:n { \prop_get:NeNTF \l_xpg_langsetup_prop {#1/direction} \l_tmpa_tl { \__xpg_set_par_direction:n{\l_tmpa_tl} } { \xpg_error_msg:n {Could~ not~ retrieve~ key~ direction~ for~ language~ "#1"} \prop_show:N{\l_xpg_langsetup_prop} } } \cs_new:Nn \__xpg_lang_set_text_direction:nn { \prop_get:NeNTF \l_xpg_langsetup_prop {#1/direction} \l_tmpa_tl { \__xpg_set_text_direction:n{\l_tmpa_tl}{#2} } { \xpg_error_msg:n {Could~ not~ retrieve~ key~ direction~ for~ language~ "#1"} \prop_show:N{\l_xpg_langsetup_prop} } } \tl_new:N \g__xpg_lastlanguage_tl \tl_set:Nn \g__xpg_lastlanguage_tl { 0 } % Track whether hyphenation is disabled \bool_new:N \g__xpg_hyphenation_disabled_bool \DeclareDocumentCommand \disablehyphenation {} { \bool_if:NTF \g_xpg_main_lang_set_bool { \__xpg_disable_hyphenation: } { % we have to postpone the execution until the main language % has been set (#125). \cs_gset_nopar:Nn \__xpg_at_begin_document_hyphenation: { \__xpg_disable_hyphenation: } } } \cs_new:Nn \__xpg_disable_hyphenation: { \bool_if:NF \g__xpg_hyphenation_disabled_bool { \bool_gset_true:N \g__xpg_hyphenation_disabled_bool \tl_gset:Ne \g__xpg_lastlanguage_tl { \the\language } % We do not call \xpg_set_hyphenation_patterns:n here to avoid a warning message. % "nohyphenation" is not listed in language.dat.lua. \language=\l@nohyphenation } } \DeclareDocumentCommand \enablehyphenation {} { \bool_if:NT \g__xpg_hyphenation_disabled_bool { \bool_gset_false:N \g__xpg_hyphenation_disabled_bool \language=\tl_use:N{\g__xpg_lastlanguage_tl} } } \cs_new:Npn \__xpg_auto_setupfont:n #1 { \bool_if:cTF { g__xpg_#1_fontsetup_bool } { \str_if_eq:eeTF{\prop_item:Nn{\l_xpg_langsetup_prop}{#1/lcscript}}{latin} {\SetupLatinPolyglossiaFont{#1}} {\SetupNonLatinPolyglossiaFont{#1}} } { \xpg_info_msg:n{Skipping~ automatic~ font~ setup~ for~ language~ #1} } } % add fontfeature Language=#2 to langtag #1 % do nothing if #1 or #2 is empty \cs_new:Nn \__xpg_add_font_feature_language:nn { \bool_if:nTF{\tl_if_empty_p:n{#1} || \tl_if_empty_p:n{#2}} { % maybe an error ? \xpg_warning_msg:n{Asking~ to~ add~ empty~ feature~to~ main~ font~ (Language="#2"~ to~ langtag~ "#1")} } { \str_if_eq:nnTF{#2}{Turkish} { \fontspec_if_language:nTF {TRK} { \addfontfeature{Language=Turkish} } { \fontspec_if_language:nT {TUR} { \addfontfeature{Language=Turkish} } } }{ \fontspec_if_language:nT{#1} { \addfontfeature{Language=#2} } } } } \cs_generate_variant:Nn \__xpg_add_font_feature_language:nn { ee } % add fontfeature Script=#3 to scripttag #2 for family #1 % do nothing if #2 or #3 is empty \cs_new:Nn \__xpg_add_font_feature_script:nnn { \bool_if:nTF{\tl_if_empty_p:n{#2} || \tl_if_empty_p:n{#3}} { % maybe an error ? \xpg_warning_msg:n{Asking~ to~ add~ empty~ feature~to~ main~ font (Script="#3"~ to~ scripttag~ "#2")} } { \fontspec_if_script:nTF{#2} {\addfontfeature{Script=#3}} { \tl_set:Nn \xpg_ffamily_tl {} \tl_set:Nn \xpg_ffamilysh_tl { #1 } \str_if_eq:nnT { #1 } { rm } { \tl_set:Nn \xpg_ffamily_tl { roman } \tl_set:Nn \xpg_ffamilysh_tl {} } \str_if_eq:nnT { #1 } { sf } { \tl_set:Nn \xpg_ffamily_tl { sans~ serif } } \str_if_eq:nnT { #1 } { tt } { \tl_set:Nn \xpg_ffamily_tl { monospace } } % Strip font family name for error message % Courtesy of egreg, https://tex.stackexchange.com/a/613996 \str_set:Nx \xpg_fname_str { \fontname\font } % Remove all after : \regex_replace_once:nnN { \:.* } { } \xpg_fname_str % ... and all after / \regex_replace_once:nnN { /.* } { } \xpg_fname_str % ... and brackets \regex_replace_once:nnN { \[ } { } \xpg_fname_str \regex_replace_once:nnN { \] } { } \xpg_fname_str % ... and extensions \regex_replace_once:nnN { \.[^\.]* \Z } { } \xpg_fname_str % ... and, finally, quotation marks \regex_replace_once:nnN { " } { } \xpg_fname_str \xpg_error_msg:n { The~ current~ main ~ \xpg_ffamily_tl\space font,~ \xpg_fname_str,~ does~ not~ contain~ the~"#3"~ script! \iow_newline: Please~ define~\csname\tl_if_empty:nF{#3}{\str_lowercase:n{#3}}font\xpg_ffamilysh_tl\endcsname~ with~ \string\newfontfamily\space command } } } } \cs_generate_variant:Nn \__xpg_add_font_feature_script:nnn { nee } %% TODO: probably can be cleaned a little more \cs_new_protected:Npn \__xpg_setup_font:nnnnn #1 #2 #3 #4 #5 % #1 = lang, #2 = family, #3 = family, #4 = gobble, #5 gobble { \cs_set_protected_nopar:cpn { #1@font@#2 } { \cs_if_exist_use:cF{ #1font#3 } { \cs_if_exist_use:cF { \prop_item:Nn \l_xpg_langsetup_prop { #1 / lc-bcp47-script } font#3 } { #4 { \prop_item:Nn \l_xpg_langsetup_prop { #1 / lcscript } font#3 } { \use:c { #2familylatin } #5 { \__xpg_add_font_feature_script:nee { #2 } { \prop_item:Nn \l_xpg_langsetup_prop { #1 / scripttag } } { \prop_item:Nn \l_xpg_langsetup_prop { #1 / script } } } } } \__xpg_add_font_feature_language:ee { \prop_item:Nn \l_xpg_langsetup_prop { #1 / langtag } } { \prop_item:Nn \l_xpg_langsetup_prop { #1 / language } } } \tl_set:Nn \familytype { #2 } } } \NewDocumentCommand \SetupLatinPolyglossiaFont { m } { \__xpg_setup_font:nnnnn { #1 } { rm } { } { \use_ii:nn } { \use_none:n } \__xpg_setup_font:nnnnn { #1 } { sf } { sf } { \use_ii:nn } { \use_none:n } \__xpg_setup_font:nnnnn { #1 } { tt } { tt } { \use_ii:nn } { \use_none:n } } \NewDocumentCommand \SetupNonLatinPolyglossiaFont { m } { \__xpg_setup_font:nnnnn { #1 } { rm } { } { \cs_if_exist_use:cF } { \use:n } \__xpg_setup_font:nnnnn { #1 } { sf } { sf } { \cs_if_exist_use:cF } { \use:n } \__xpg_setup_font:nnnnn { #1 } { tt } { tt } { \cs_if_exist_use:cF } { \use:n } } %%% END OF PolyglossiaSetup %% ensure localization of \markright and \markboth commands %%% THIS IS NOW DISABLED BY DEFAULT \cs_new_nopar:Nn \__xpg_local_marks:n { } \cs_new_nopar:Nn \__xpg_enable_local_marks: { \xpg_info_msg:n{Option:~ localmarks} \cs_gset_nopar:Nn \__xpg_local_marks:n { \DeclareDocumentCommand \markboth { m m } { \group_begin: \cs_set_eq:cc { label } { relax } \cs_set_eq:cc { index } { relax } \cs_set_eq:cc { glossary } { relax } \unrestored@protected@xdef\@themark { {\foreignlanguage{##1}{\protect\@@ensure@maindir{####1}}} {\foreignlanguage{##1}{\protect\@@ensure@maindir{####2}}} } \@temptokena \expandafter{\@themark} \mark_insert:nn{2e-left}{####1} \mark_insert:nn{2e-right}{####2} \tl_if_empty:nF{####2}{ \mark_insert:nn{2e-right-nonempty}{####2} } \mark{\the\@temptokena} \group_end: \if@nobreak\ifvmode\nobreak\fi\fi } \DeclareDocumentCommand \markright { m } { \group_begin: \cs_set_eq:cc { label } { relax } \cs_set_eq:cc { index } { relax } \cs_set_eq:cc { glossary } { relax } \expandafter\@markright\@themark {\foreignlanguage{##1}{\protect\@@ensure@maindir{####1}}} \@temptokena \expandafter{\@themark} \mark_insert:nn{2e-right}{####1} \tl_if_empty:nF{####1}{ \mark_insert:nn{2e-right-nonempty}{####1} } \mark{\the\@temptokena} \group_end: \if@nobreak\ifvmode\nobreak\fi\fi } } } %we call this macro when a gloss file is not found for a given language \cs_new_nopar:Nn \__xpg_no_gloss:n { \xpg_warning_msg:n {File~ gloss-#1.ldf~ do~ not~ exists! \iow_newline: I~ will~ nevertheless~ try~ to~ use~ hyphenation~ patterns~ for~ #1.} \PolyglossiaSetup{#1}{hyphenmins={2,3},hyphennames={#1},fontsetup=true} % the above amounts to: %\ifcsundef{l@#1}% % {\expandafter\adddialect\csname l@#1\endcsname\l@nohyphenation\relax}% % {\setlocalhyphenmins{#1}{2}{3}}% %\csdef{#1@language}{\language=\csname l@#1\endcsname}% } \cs_new_nopar:Nn \xpg_input:n { % Store catcode of @ before making at letter \cs_set_protected_nopar:Npx \__xpg_restore_at_catcode: { \char_set_catcode:nn { `@ } { \char_value_catcode:n {`\@ } } } \char_set_catcode_letter:N @ \file_input:n { #1 } % restore former @ catcode \__xpg_restore_at_catcode: } % try to load a language file \cs_new:Nn \__xpg_load_lang_definition:nn { \file_if_exist:nTF{gloss-#2.ldf} { \tl_set:Nn \xpg__tmp_default_options_tl { #1 } % Temporarily force catcode of ~ to 13 (active) since babelsh.def % requires it. This is needed particularly with LaTeX3 % packages which force \ExplSyntaxOn (#425) \cs_gset_protected:Npx \__xpg_restore_tilde_catcode: { \char_set_catcode:nn { 126 } { \char_value_catcode:n { 126 } } } \char_set_catcode_active:n { 126 } \xpg_input:n {gloss-#2.ldf} % restore former ~ catcode \__xpg_restore_tilde_catcode: } { \__xpg_no_gloss:n {#2} } } \cs_generate_variant:Nn \__xpg_load_lang_definition:nn { ee } % load a master language from an alias file \NewDocumentCommand \InheritGlossFile { m } { \seq_if_in:NeF \__xpg_langs_loaded {#1} { \xpg_input:n {gloss-#1.ldf} % define environment and command if not alias \str_if_eq:eeT {\prop_item:Ne \__xpg_alias {#1/target}} {#1} { \use:c{\prop_item:Nn{\l_xpg_langsetup_prop} {#1/DefineCommandsCmd}} {#1} } \seq_gput_right:Nn \__xpg_langs_loaded {#1} } \__xpg_register_language:nn{}{#1} } \prop_new_linked:N \__xpg_alias % define environment and command if not alias \cs_new:Nn \xpg_define_language_commands:n { \str_if_eq:eeT {\prop_item:Ne \__xpg_alias {#1/target}} {#1} { \exp_args:Ne \NewDocumentEnvironment {\prop_item:Nn{\l_xpg_langsetup_prop}{#1/envname}} { O{} } { \otherlanguage [ ##1 ] { #1 } } { \endotherlanguage } \exp_args:Nc \NewDocumentCommand {text#1} { O{} m } { \__xpg_textlanguage:een{##1}{#1}{##2} } } } \cs_generate_variant:Nn \xpg_define_language_commands:n {e} % resolve alias property #1 lang #2 item \cs_new:Nn \xpg_alias_prop_item:nn { \prop_if_in:NeTF \__xpg_alias {#1/#2} { \prop_item:Ne \__xpg_alias {#1/#2} } { \prop_if_in:NeTF \__xpg_alias {#1/target} { % target to self fall back to language table \str_if_eq:eeTF { \prop_item:Ne \__xpg_alias {#1/target} } { #1 } { \prop_item:Nn{\l_xpg_langsetup_prop} {#1/#2} } % load alias by recursion { \xpg_alias_prop_item:ee { \prop_item:Ne \__xpg_alias {#1/target} } {#2} } } { % empty } } } \cs_generate_variant:Nn \xpg_alias_prop_item:nn {en, ne, ee} % add option #2 to list of option of language #1 \cs_new:Nn \xpg_alias_add_to_option_i:nn { \tl_if_blank:eTF {#2} { \xpg_alias_prop_item:nn {#1}{options} } { \tl_if_blank:eTF { \xpg_alias_prop_item:nn {#1}{options} } { #2 } { \xpg_alias_prop_item:nn {#1}{options},#2 } } } % get base language \cs_new:Nn \xpg_alias_base_lang:n { \prop_item:Ne \__xpg_alias {#1/target} } \cs_generate_variant:Nn \xpg_alias_base_lang:n {e} \keys_define:nn { polyglossia/alias } { % babelname\l_tmpa_prop babelname .prop_put:N = \__xpg_language_alias_prop, % bcp47 bcp47 .prop_put:N = \__xpg_language_alias_prop, % variant variant .prop_put:N = \__xpg_language_alias_prop, } % provide way to define alias environment and command % #1 () variant % #2 [] option (not yet without variant and bcp47 name) % #3 language % #4 () babel name % #5 [] bcp47 name % #6 alias \DeclareDocumentCommand \xpg_language_alias { D(){} O{} m D(){} O{} m} { \prop_gremove:Nn \__xpg_alias {#6/target} \prop_gremove:Nn \__xpg_alias {#6/options} \prop_gremove:Nn \__xpg_alias {#6/bcp47} \prop_gremove:Nn \__xpg_alias {#6/babelname} \prop_gremove:Nn \__xpg_alias {#6/variant} \prop_gput:Nee \__xpg_alias {#6/target} {#3} \tl_if_blank:eF {#1} { \prop_gput:Nee \__xpg_alias {#6/variant} {#1} } \tl_if_blank:eF {#5} { \prop_gput:Nee \__xpg_alias {#6/bcp47} {#5} } \tl_if_blank:eF {#4} { \prop_gput:Nee \__xpg_alias {#6/babelname} {#1} } \tl_if_blank:eF {#2} { \prop_gput:Nee \__xpg_alias {#6/options} {#2} } } % provide way to define alias environment and command % \setlanguagealias[]{}{} \DeclareDocumentCommand \setlanguagealias {s O{} m m} { % The starred version does not define commands and environments \IfBooleanF {#1} { \exp_args:Nc \DeclareDocumentCommand {text#4} { O{} m } { \__xpg_textlanguage:een {##1} {#4} {##2} } \DeclareDocumentEnvironment { #4 } { } { \otherlanguage { #4 } } { \endotherlanguage } } \tl_clear_new:N \__xpg_alias_option_tl \prop_clear_new:N \__xpg_language_alias_prop \keys_set_known:nnN{polyglossia/alias} {#2} \__xpg_alias_option_tl \xpg_language_alias (\prop_item:Nn \__xpg_language_alias_prop {variant}) % TODO not yet [\__xpg_alias_option_tl] [#2] {#3} (\prop_item:Nn \__xpg_language_alias_prop {babelname}) [\prop_item:Nn \__xpg_language_alias_prop {bcp47}] {#4} } \cs_new:Nn \__xpg_register_language:nn { \clist_if_in:NeF \xpg@loaded {#2}{ \clist_gput_right:Ne \xpg@loaded {#2} } \group_begin: % set language options \__xpg_set_language_options:nn {#2} {#1} % register babelname \prop_get:NeN \l_xpg_langsetup_prop {#2/babelname} \l_tmpa_tl \clist_if_in:NeF \xpg@bloaded {\l_tmpa_tl}{ \clist_gput_right:Ne \xpg@bloaded {\l_tmpa_tl} } % register BCP-47 ID \prop_get:NeN \l_xpg_langsetup_prop {#2/bcp47} \l_tmpa_tl \clist_if_in:NeF \xpg@bcp@loaded {\l_tmpa_tl}{ \clist_gput_right:Ne \xpg@bcp@loaded {\l_tmpa_tl} } % register variant \prop_get:NnNT \l_xpg_curropt_prop {#2/variant} \l_tmpa_tl { \clist_if_in:NeF \xpg@vloaded {\l_tmpa_tl}{ \clist_gput_right:Ne \xpg@vloaded {\l_tmpa_tl} }} \group_end: } \prop_new_linked:N \l_xpg_curropt_prop \DeclareDocumentCommand \setdefaultlanguage { O{} m } { % latex is an internal language, so do not record \str_if_eq:eeF{#2}{latex} { \clist_if_in:NeF \xpg@loaded {\xpg_alias_base_lang:n{##2}}{ \clist_gput_right:Ne \xpg@loaded {\xpg_alias_base_lang:n{##2}} } } \seq_if_in:NeF \__xpg_langs_loaded {#2} { \__xpg_load_lang_definition:nn{#1}{#2} % define environment and command if not alias \str_if_eq:eeT {\prop_item:Ne \__xpg_alias {#2/target}} {#2} { \use:c{\prop_item:Ne{\l_xpg_langsetup_prop} {#2/DefineCommandsCmd}} {#2} } \seq_gput_right:Ne \__xpg_langs_loaded {#2} } \cs_set_nopar:Npe \mainlanguagevariant { \prop_item:Ne \l_xpg_curropt_prop { \xpg_alias_base_lang:n{#2} / variant } } \exp_args:Nee \__xpg_set_default_language:nn {\xpg_alias_add_to_option_i:nn{#2}{#1}} {\xpg_alias_base_lang:n{#2}} } \cs_new:Nn \__xpg_set_default_language:nn { \tl_gset:Nn \xpg_main_language_tl {#2} %% The following settings are for the default language and script % this tells bidi.sty or luabidi.sty that the document is RTL \__xpg_if_LR_str:eF{\prop_item:Nn{\l_xpg_langsetup_prop}{#2/direction}} { \__xpg_setRTLmain: } \cs_gset_nopar:Nn \__xpg_at_begin_document_selectlanguage: { \selectbackgroundlanguage{#2} \selectlanguage[#1]{#2} } \str_if_eq:eeF { #2 } { latex } { \xpg_info_msg:n{Default~ language~ is~ #2} } \xpg_set_language_name:n { #2 } \cs_gset_nopar:Npn \mainlanguagename {#2} } \DeclareCommandCopy \setmainlanguage \setdefaultlanguage % Returns the language ID of the current language % Currently supported: bcp-47 \DeclareDocumentCommand \languageid {m} { \str_case:nnF {#1} { {bcp-47} { \use:c{bcp47.tag} } {bcp47} { \use:c{bcp47.tag} } } { \xpg_ill_arg_msg:nn { #1 } { \languageid } } } % Returns the language ID of the main language % Currently supported: bcp-47 \DeclareDocumentCommand \mainlanguageid {m} { \str_case:nnF {#1} { {bcp-47} { \use:c{bcp47.main.tag} } {bcp47} { \use:c{bcp47.main.tag} } } { \xpg_ill_arg_msg:nn { #1 } { \mainlanguageid } } } % Kernel command to access to BCP-47 data. % Shared interface with babel. % We support: % * language (e.g., de) % * region (e.g., AT) % * script (e.g., Latn) % * variant (e.g., 1901) % * extension-t (transformation, e.g., en-t-ja) % * extension-u (additional locale information, e.g., ar-u-nu-latn) % * extension-x (e.g., classic for la-x-classic) % * casing (whatever is suitable for \MakeUppercase and friends, % usually alias to language but could also be something % like el-x-iota or ckb-Latn) % * tag (the registered full tag) % and main.* variants thereof % See https://github.com/latex3/latex2e/issues/1035 \DeclareExpandableDocumentCommand \BCPdata {m} { \cs_if_exist_use:cF{bcp47.#1} { \xpg_ill_arg_msg:nn { #1 } { \BCPdata } } } \clist_map_inline:nn { language, region, script, variant, extension.t, extension.u, extension.x }{ \tl_set:Nn \l_tmpa_tl { #1 } \tl_replace_once:Nnn \l_tmpa_tl { . } { - } \cs_gset_nopar:cpe { bcp47.#1 } { \exp_not:n { \prop_item:Ne \l_xpg_langsetup_prop } { \exp_not:N \languagename / bcp47-\l_tmpa_tl } } \cs_gset_nopar:cpe { bcp47.main.#1 } { \exp_not:n { \prop_item:Ne \l_xpg_langsetup_prop } { \exp_not:N \mainlanguagename / bcp47-\l_tmpa_tl } } } \cs_gset_nopar:cpn { bcp47.casing } { \tl_if_empty:eTF { \prop_item:Ne \l_xpg_langsetup_prop { \languagename / bcp47-casing } } { \prop_item:Ne \l_xpg_langsetup_prop { \languagename / bcp47-language } }{ \prop_item:Ne \l_xpg_langsetup_prop { \languagename / bcp47-casing } } } \cs_gset_nopar:cpn { bcp47.main.casing } { \tl_if_empty:eTF { \prop_item:Ne \l_xpg_langsetup_prop { \mainlanguagename / bcp47-casing } } { \prop_item:Ne \l_xpg_langsetup_prop { \mainlanguagename / bcp47-language } }{ \prop_item:Ne \l_xpg_langsetup_prop { \mainlanguagename / bcp47-casing } } } \cs_gset_nopar:cpn { bcp47.tag } { \prop_item:Ne \l_xpg_langsetup_prop { \languagename / bcp47 } } \cs_gset_nopar:cpn { bcp47.main.tag } { \prop_item:Ne \l_xpg_langsetup_prop { \mainlanguagename / bcp47 } } \cs_new_nopar:Npn \languagevariant { \prop_item:Ne \l_xpg_curropt_prop { \languagename / variant } } \cs_new:Nn \xpg_set_language_name:n { \cs_set:Npn \languagename { #1 } } \NewDocumentCommand \resetdefaultlanguage { O{} m } { \__xpg_reset_default_language:nn {\xpg_alias_add_to_option_i:nn{#2}{#1}} {\xpg_alias_base_lang:n{#2}} } \cs_new:Nn \__xpg_reset_default_language:nn { \xpg_error_if_lang_not_loaded:n{#2} % disable globalnumbers of previously defined default language \use:c{no\xpg_main_language_tl @globalnumbers} \use:c{noextras@\xpg_main_language_tl} % This is a hook for external packages which want to access variants % via babelname (such as biblatex) \cs_if_exist_use:c{noextras@bbl@\mainbabelname} \use:c{init@noextras@\xpg_main_language_tl} \xpg_set_language_name:n { #2 } \__xpg_if_LR_str:eF{\prop_item:Ne{\l_xpg_langsetup_prop}{#2/direction}} { \@rlmaintrue\@rl@footnotetrue } \selectlanguage[#1]{#2} \selectbackgroundlanguage{#2} \cs_set_nopar:Npe \mainlanguagevariant { \prop_item:Ne \l_xpg_curropt_prop { \xpg_alias_base_lang:n{#2} / variant } } } % This saves the normalfont for the latin script since we may change normalfont in other scripts \cs_set_eq:cc { normalfontlatin } { normalfont } % Provide default fonts (as set with \setmainfont, \setsansfont and \setmonofont) % for Latin scripts and as a fallback for non-Latin scripts. \cs_set_protected:Nn \xpg_defaultfont_rm: { \tl_if_empty:NF{\g__fontspec_nfss_enc_tl}{\fontencoding{\g__fontspec_nfss_enc_tl}} \fontfamily\rmdefault \hook_use:n { rmfamily } \selectfont } \cs_set_protected:Nn \xpg_defaultfont_sf: { \tl_if_empty:NF{\g__fontspec_nfss_enc_tl}{\fontencoding{\g__fontspec_nfss_enc_tl}} \fontfamily\sfdefault \hook_use:n { sffamily } \selectfont } \cs_set_protected:Nn \xpg_defaultfont_tt: { \tl_if_empty:NF{\g__fontspec_nfss_enc_tl}{\fontencoding{\g__fontspec_nfss_enc_tl}} \fontfamily\ttdefault \hook_use:n { ttfamily } \selectfont } \cs_new:Nn \__xpg_patch_fontfamilies: { % This robustifies the redefinitions of \family (suggestion by Enrico Gregorio) % e.g. to prevent expansion of the \familytype redefinition in auxiliary files \tl_put_right:cn {rmfamily~} {\tl_set:Nn \familytype {rm}} \tl_put_right:cn {sffamily~} {\tl_set:Nn \familytype {sf}} \tl_put_right:cn {ttfamily~} {\tl_set:Nn \familytype {tt}} } % These switches activate the default fonts % Note that a simple \let\rmfamilylatin=\rmfamily % does not work reliably (see #24) \cs_set_eq:cc{rmfamilylatin}{xpg_defaultfont_rm:} \cs_set_eq:cc{sffamilylatin}{xpg_defaultfont_sf:} \cs_set_eq:cc{ttfamilylatin}{xpg_defaultfont_tt:} \cs_new:Nn \xpg_set_familydefault: { \tl_set:Ne \l_tmpa_tl { \familydefault } \tl_set:Ne \l_tmpb_tl { \sfdefault } \tl_if_eq:NNTF \l_tmpa_tl \l_tmpb_tl { \tl_set:Nn \familytype {sf} } { \tl_set_eq:NN \l_tmpb_tl \ttdefault \tl_if_eq:NNTF \l_tmpa_tl \l_tmpb_tl { \tl_set:Nn \familytype {tt} } { \tl_set:Nn \familytype {rm} } } \__xpg_patch_fontfamilies: % This (re-)saves the normalfont for the latin script since we may % change normalfont in other scripts \cs_set_eq:cc{ normalfontlatin }{ normalfont } % And for all cases, we also reset \familylatin \cs_set_eq:cc{ rmfamilylatin }{ xpg_defaultfont_rm: } \cs_set_eq:cc{ sffamilylatin }{ xpg_defaultfont_sf: } \cs_set_eq:cc{ ttfamilylatin }{ xpg_defaultfont_tt: } } \cs_set_nopar:Nn \xpg_select_fontfamily:n { \str_if_eq:VnTF \familytype { tt } { \__xpg_use_or_warn:c { #1@font@tt } } { \str_if_eq:VnTF \familytype { sf } { \__xpg_use_or_warn:c { #1@font@sf } } { \__xpg_use_or_warn:c { #1@font@rm } } } } \cs_set_protected:Npn \xpg_select_default_fontfamily:n #1 { \tl_set:Ne \l_tmpa_tl { \familydefault } \tl_set:Ne \l_tmpb_tl { \sfdefault } \tl_if_eq:NNTF \l_tmpa_tl \l_tmpb_tl { \tl_set:Nn \familytype {sf} } { \tl_set_eq:NN \l_tmpb_tl \ttdefault \tl_if_eq:NNTF \l_tmpa_tl \l_tmpb_tl { \tl_set:Nn \familytype {tt} } { \tl_set:Nn \familytype {rm} } } \xpg_select_fontfamily:n{#1} } \cs_new_nopar:Nn \xpg_set_normalfont:n { \cs_set_eq:cc { rmfamily } { #1@font@rm } \cs_set_eq:cc { sffamily } { #1@font@sf } \cs_set_eq:cc { ttfamily } { #1@font@tt } \cs_set_nopar:Npn \normalfont { \xpg_select_default_fontfamily:n {#1} \fontseries{\seriesdefault}\selectfont \fontshape{\shapedefault} \hook_use:n { normalfont } \selectfont } \cs_set_nopar:Npn \reset@font {\protect\normalfont} } \cs_gset_eq:cc { @@fterindentfalse } { @afterindentfalse } \cs_new_nopar:Nn \__xpg_french_indent: { \cs_set_eq:cc { @afterindentfalse } { @afterindenttrue } \@afterindenttrue } \cs_new_nopar:Nn \__xpg_no_french_indent: { \cs_set_eq:cc { @afterindentfalse } { @@fterindentfalse } \@afterindentfalse } \DeclareDocumentCommand \selectbackgroundlanguage { m } { \__xpg_select_background_language:n {\xpg_alias_base_lang:n{#1}} } \cs_new:Nn \__xpg_select_background_language:n { \str_if_eq:eeTF { \prop_item:Nn{\l_xpg_langsetup_prop}{#1/lcscript} } { latin } {} { \xpg_set_normalfont:n{#1} } \use:c{#1@globalnumbers} } \cs_generate_variant:Nn \__xpg_select_background_language:n {e} % Declare secondary language #2 with language options #1 \DeclareDocumentCommand \setotherlanguage { O{} m } { \seq_if_in:NeF \__xpg_langs_loaded {#2} { \__xpg_load_lang_definition:ee {#1} {#2} % define environment and command if not alias \str_if_eq:eeT {\prop_item:Ne \__xpg_alias {#2/target}} {#2} { \use:c{\prop_item:Ne{\l_xpg_langsetup_prop} {#2/DefineCommandsCmd}} {#2} } \__xpg_set_otherlanguage:ee {\xpg_alias_add_to_option_i:nn{#2}{#1}} {\xpg_alias_base_lang:n{#2}} \seq_gput_right:Ne \__xpg_langs_loaded {#2} } } \cs_new:Nn \__xpg_set_otherlanguage:nn { \__xpg_register_language:nn{#1}{#2} } \cs_generate_variant:Nn \__xpg_set_otherlanguage:nn { ee } \NewDocumentCommand \setotherlanguages { m } { \clist_map_function:eN { #1 } \setotherlanguage } \cs_set:Nn \xpg_common_language: {% FIXME is this really needed??? \bool_if:NTF \g__xpg_hyphenation_disabled_bool { \tl_gset:Ne \g__xpg_lastlanguage_tl {\z@} }{ \language=\z@ } \lefthyphenmin=\tw@ \righthyphenmin=\thr@@} \cs_set:Nn \xpg_initial_setup: { \xpg_common_language: } % Alias to \text, but more suitable % for specific (esp. tag-based) aliases % where \text would cause clashes % (e.g., \textit) \NewDocumentCommand \textlang { O{} m +m } { \__xpg_textlanguage:een {#1} {#2} {#3} } % prevent the language tag in \textlang % (second argument) from being affected % inside case changing commands (e.g. \MakeUppercase) \tl_put_right:Nn \l_text_case_exclude_arg_tl { \textlang } % wrapper for foreignlanguage and otherlanguage* \cs_new_nopar:Nn \xpg_set_foreign_language:nn { \xpg_select_language:nn { #1 } { #2 } \__xpg_register_language:nn{#1}{#2} } % lowercase options before passing to setkeys \NewDocumentCommand \SetGlossOptions { m m } { % \text_lowercase:n fully expands % (as opposed to \str_lowercase:n) \use:c { xpg_#1_default_options_tl } \exp_args:Ne \keys_set:ne{ polyglossia / gloss / #1 }{ \text_lowercase:n {#2} } } % joint code of \foreignlanguage, otherlanguage* % and \text % #1 option % #2 language \cs_new:Nn \xpg_otherlanguage:nn { \xpg_error_if_lang_not_loaded:n{#2} \SetGlossOptions{#2}{#1} \xpg_set_foreign_language:nn { #1 } { #2 } % Hook for external packages such as biblatex \polyglossia@language@switched % buggy restoration heure \use:c{inlineextras@#2} % This is a hook for external packages which want to access variants % via babelname (such as biblatex) \cs_if_exist_use:c{inlineextras@bbl@\babelname} } \DeclareDocumentCommand { \foreignlanguage } { O{} m +m } { \__xpg_foreignlanguage:eeen {#1} {#2} {\xpg_alias_base_lang:n{#2}} {#3} } % prevent case changing of language name in \foreignlanguage \tl_put_right:Nn \l_text_case_exclude_arg_tl { \foreignlanguage } % Direct access to \xpg_set_foreign_language:nn % used in captions \NewDocumentCommand \setforeignlanguage { O{} m } { \xpg_set_foreign_language:nn { #1 } { #2 } } % internal wrapper for foreign language % #1 option % #2 alias % #3 base lang % #4 text \cs_new:Nn \__xpg_foreignlanguage:nnnn { \tl_if_blank:nTF {#3} { \msg_show:nnn { polyglossia } { languagenotloaded } {#2} }{ \group_begin: \xpg_otherlanguage:nn{ \xpg_alias_add_to_option_i:nn{#2}{#1} }{ #3 } \__xpg_lang_set_text_direction:nn{#3}{#4} \group_end: } } \cs_generate_variant:Nn \__xpg_foreignlanguage:nnnn {eeen} % otherlanguage* is the environment equivalent of \foreignlanguage \DeclareDocumentEnvironment { otherlanguage* } { O{} m } { \__xpg_otherlanguage:eee { #1 } { #2 } { \xpg_alias_base_lang:n { #2 } } }{ \c_group_end_token% \group_end: does not work here! } % internal wrapper % #1 option % #2 alias % #3 base lang \cs_new:Nn \__xpg_otherlanguage:nnn { \tl_if_blank:nTF {#3} { \msg_show:nnn { polyglossia } { languagenotloaded } {#2} }{ \xpg_otherlanguage:nn{ \xpg_alias_add_to_option_i:nn{#2}{#1} }{ #3 } \__xpg_lang_set_text_direction:nn{#3}% \c_group_begin_token% \group_begin: does not work here! } } \cs_generate_variant:Nn \__xpg_otherlanguage:nnn { eee } % use by \text and \textlang. Equivalent to \foreignlanguage, % except that dates are localized. % #1: option % #2: alias % #3: text \cs_new:Nn \__xpg_textlanguage:nnn { \__xpg_textlanguage:nnen {#1} {#2} {\xpg_alias_base_lang:n{#2}} {#3} } \cs_generate_variant:Nn \__xpg_textlanguage:nnn {een} % use by \text and \textlang. Equivalent to \foreignlanguage, % except that dates are localized. % #1: option % #2: alias % #3: base language % #4: text \cs_new:Nn \__xpg_textlanguage:nnnn { \tl_if_blank:nTF {#3} { \msg_show:nnn { polyglossia } { languagenotloaded } {#2} } { \group_begin: \xpg_otherlanguage:nn{#1}{#3} \use:c{date#3} % This is a hook for external packages which want to access variants % via babelname (such as biblatex) \cs_if_exist_use:c{date@bbl@\babelname} \__xpg_lang_set_text_direction:nn{#3}{#4} \group_end: } } \cs_generate_variant:Nn \__xpg_textlanguage:nnnn {nnen} % Define language-specific hyphenation exceptions \NewDocumentCommand \pghyphenation {O{} m m} { \c_group_begin_token \xpg_error_if_lang_not_loaded:n { #2 } \SetGlossOptions{#2}{#1} \xpg_select_language:nn { #1 } { #2 } \hyphenation{#3} \c_group_end_token } % Hook that other package authors can use % (for instance biblatex): % Do not rename! \cs_set_nopar:Npn \xpg@hook@setlanguage {} \cs_set_nopar:Nn \__xpg_pop_language:nn { \xpg_set_language_aux:nn { #1 } { #2 } \xpg@hook@setlanguage % FIXME This seems to be a very old relict. % The macro is nowhere used. Probably remove. % \let\emp@langname\@undefined } \DeclareDocumentCommand \selectlanguage {s O{} m} { \tl_if_blank:eTF {\xpg_alias_base_lang:n{#3}} { \IfBooleanTF { #1 } { \msg_show:nnn { polyglossia } { languagenolongerloaded } {#3} } { \msg_show:nnn { polyglossia } { languagenotloaded } {#3} } }{ \__xpg_select_language:nee {#1} { \xpg_alias_add_to_option_i:nn{#3}{#2} } { \xpg_alias_base_lang:n{#3} } } } \cs_new:Nn \__xpg_select_language:nnn { % Register the language options \__xpg_set_language_options:nn {#3} {#2} \IfBooleanTF { #1 } % The starred variant does not write to the aux { \xpg_set_language_nonaux:nn { #2 } { #3 } }{ \cs_set_nopar:Ne \xpg_pop_language: { \exp_not:N \__xpg_pop_language:nn { #2 } { #3 } } \group_insert_after:N \xpg_pop_language: \xpg_set_language_aux:nn { #2 } { #3 } } \__xpg_register_language:nn { #2 } { #3 } } \cs_generate_variant:Nn \__xpg_select_language:nnn { nee, nne } % set lang option #2 for lang #1 \cs_new:Nn \__xpg_set_language_options:nn { \cs_if_exist:cT { xpg_#1_default_options_prop } { \prop_concat:ccc { l_xpg_curropt_prop } { l_xpg_curropt_prop } { xpg_#1_default_options_prop } } \xpg__keyval_parser:eeN { #2 } { #1 } \l_xpg_curropt_prop \SetGlossOptions{#1}{#2} } % Initialize default language options, so that % \iflanguageoption has the info it needs also % for default settings \NewDocumentCommand \InitializeGlossOptions { m m } { \tl_new:c { xpg_#1_default_options_tl } \prop_new:c { xpg_#1_default_options_prop } \keys_precompile:nec { polyglossia / gloss / #1 } { \text_lowercase:n { #2, \xpg__tmp_default_options_tl } } { xpg_#1_default_options_tl } \xpg__keyval_parser:enc { #2, \xpg__tmp_default_options_tl } { #1 } { xpg_#1_default_options_prop } \prop_concat:ccc { l_xpg_curropt_prop } { l_xpg_curropt_prop } { xpg_#1_default_options_prop } \use:c { xpg_#1_default_options_tl } \__xpg_set_language_options:nn {#1} {#2} } \tl_new:N \xpg__tmp_default_options_tl \cs_generate_variant:Nn \keys_precompile:nnN { nec } % Record synonymous keyvals such as variant=us and variant=american % Syntax: \SetLanguageAliasValues{}{}{} \int_new:N \l_xpg_alias_keyvals_int \int_set:Nn \l_xpg_alias_keyvals_int { 2 } \NewDocumentCommand \SetLanguageAliasValues { m m m } { \clist_map_inline:nn { #3 } { \int_const:cn { c_xpg_alias_keyvals_#1_#2_##1_int } { \l_xpg_alias_keyvals_int } } \int_incr:N \l_xpg_alias_keyvals_int } \cs_new:Npn \xpg__keyval_parser:nnN #1 #2 #3 % #1 = key-vals, #2 = language, #3 = prop { \keyval_parse:nnn { \xpg__keyval_parser_default:nnn { #2 } { #3 } } { \xpg__keyval_parser_nondefault:nnnn { #2 } { #3 } } { #1 } } \cs_generate_variant:Nn \xpg__keyval_parser:nnN { eeN, enc } \cs_new:Npn \xpg__keyval_parser_default:nnn #1 #2 #3 % #1 = lang, #2 = prop, #3 = key { \str_set:Nn \l_tempa_str { #1 / #3 } \str_concat:NNN \l_tempa_str \c__keys_default_root_str \l_tempa_str \prop_put:Nne #2 { #1 / #3 } { \use:c { \l_tempa_str } } } \cs_new:Npn \xpg__keyval_parser_nondefault:nnnn #1 #2 #3 #4 % #1 = lang, #2 = prop, #3 = key, #4 = value { \prop_put:Nnn #2 { #1 / #3 } { #4 } } \prg_set_conditional:Npnn \__xpg_check_option_value:NNN #1#2#3 { p , T , F , TF } { \bool_lazy_or:nnTF { \str_if_eq_p:ee { \prop_item:Nn \l_xpg_curropt_prop { #1 / #2 } } { #3 } } { \int_compare_p:nNn { \cs_if_exist_use:cF { c_xpg_alias_keyvals_#1_#2_#3_int } { 0 } } = { \cs_if_exist_use:cF { c_xpg_alias_keyvals_#1_#2_\prop_item:Nn \l_xpg_curropt_prop { #1 / #2 }_int } { 1 } } } { \prg_return_true: } { \prg_return_false: } } \prg_set_conditional:Npnn \xpg_if_main_language:n #1 { T, F, TF } { \str_if_eq:VnTF \xpg_main_language_tl { #1 } { \prg_return_true: } { \prg_return_false: } } \cs_set_eq:NN \IfMainLanguageTF \xpg_if_main_language:nTF \cs_set_eq:NN \IfMainLanguageT \xpg_if_main_language:nT \cs_set_eq:NN \IfMainLanguageF \xpg_if_main_language:nF % Test if option value is set \DeclareDocumentCommand \iflanguageoption { m m m m m } { \__xpg_check_option_value:NNNTF{#1}{#2}{#3}{#4}{#5} } % Test if language is loaded \DeclareDocumentCommand \iflanguageloaded { m m m } { \hook_gput_code:nnn {begindocument/end} {.} { \clist_if_in:NeTF \xpg@loaded{#1}{#2}{#3} } } % Same for babellanguage is loaded \DeclareDocumentCommand \ifbabellanguageloaded { m m m } { \hook_gput_code:nnn {begindocument/end} {.} { \clist_if_in:NeTF \xpg@bloaded{#1}{#2}{#3} } } % Same for languageid \DeclareDocumentCommand \iflanguageidloaded { m m m m } { \hook_gput_code:nnn {begindocument/end} {.} { \str_case:nnTF {#1} { {bcp-47} { \clist_if_in:NeTF \xpg@bcp@loaded{#2}{#3}{#4} } {bcp47} { \clist_if_in:NeTF \xpg@bcp@loaded{#2}{#3}{#4} } } {} { \xpg_ill_arg_msg:nn { #1 } { \iflanguageidloaded } } }% } % Check if the current font has a given glyph \prg_new_conditional:Npnn \__xpg_if_char:N #1 { TF } { \iffontchar\font\int_from_hex:n { #1 }~ \prg_return_true: \else: \prg_return_false: \fi: } % Test if a char (by char code) is available in the current font % and print it, if so, otherwise print the replacement #2 \NewExpandableDocumentCommand \charifavailable { m m } { \exp_args:Nno \__xpg_if_char:NTF { #1 } { \Uchar"#1 } { #2 } } % Test if a char (by char code) is available in the current font % if so, do #2, else do #3 \NewExpandableDocumentCommand \IfCharIsAvailableTF { m m m } { \__xpg_if_char:NTF { #1 } { #2 } { #3 } } \cs_new_nopar:Nn \xpg_set_language_nonaux:nn { \__xpg_start_language:nn { #1 } { #2 } } \cs_new_nopar:Nn \xpg_set_language_aux:nn { \__xpg_start_language:nn { #1 } { #2 } % Write to the aux \xpg_set_language_only_aux:nn { #1 } { #2 } } \cs_new_nopar:Nn \xpg_set_language_only_aux:nn { % Write to the aux (toc files) \if@filesw \addtocontents{toc}{\selectlanguage*[#1]{#2}} \fi } \hook_gput_code:nnn {begindocument} {.} { \if@filesw \immediate\write\@mainaux {\ProvideDocumentCommand\selectlanguage{sO{}m}{}} \fi } % Since captions might float to other language regions, % we need to change the language here (#542) \hook_gput_code:nnn {cmd/caption/before} {.} { \ifhmode\unskip\fi \addtocontents{lof}{\protect\setforeignlanguage{\languagename}} \addtocontents{lot}{\protect\setforeignlanguage{\languagename}} } % check if language is defined \prg_set_conditional:Npnn \__xpg_pattern_check_if_exists:n #1 { F, TF } { \bool_lazy_and:nnTF { \cs_if_exist_p:c { l@#1 } } { ! (\cs_if_eq_p:cc { l@#1 } { l@nohyphenation }) } { \prg_return_true: } { \prg_return_false: } } \cs_new_nopar:Nn \__xpg_luatex_load_lang:n { % if \l@#1 is not properly defined, call lua function newloader(#1), % and assign the returned number to \l@#1 \__xpg_pattern_check_if_exists:nF {#1} { \directlua { token.set_char('l@#1', polyglossia.newloader'#1') } } } % check if language is defined \prg_set_conditional:Npnn \xpg_if_language_defined:n #1 { T, F, TF } { % With luatex, we first need to define \l@#1. \sys_if_engine_luatex:T { \__xpg_luatex_load_lang:n {#1} } \__xpg_pattern_check_if_exists:nTF{#1} { \prg_return_true: } { \prg_return_false: } } % Aliases for gloss files \cs_gset_eq:cc { IfLanguageDefinedTF } { xpg_if_language_defined:nTF } \cs_gset_eq:cc { IfLanguageDefinedT } { xpg_if_language_defined:nT } \cs_gset_eq:cc { IfLanguageDefinedF } { xpg_if_language_defined:nF } % Check if patterns for language #1 is defined. If not, try % the comma-separated list of fallbacks in #2 \NewDocumentCommand \TryPatternWithFallback { m m } { \xpg_if_language_defined:nF { #1 } { \clist_clear_new:N \l_xpg_lang_patterns \clist_set:Ne \l_xpg_lang_patterns { #2 } \bool_set_false:N \l_tmpa_bool \clist_map_inline:Nn \l_xpg_lang_patterns { \xpg_if_language_defined:nT { ##1 } { \cs_gset_eq:cc { l@#1 } { l@##1 } \bool_set_true:N \l_tmpa_bool \clist_map_break: } } \bool_if:NF \l_tmpa_bool { \xpg_warning_msg:n {No~ hyphenation~ patterns~ for~ #1~ found \iow_newline: Falling~ back~ to~ the~ default~ patterns~ (=~English)!} \exp_args:Nc \adddialect {l@#1} 0 } } } % This old term is used by biblatex, so don't drop! \cs_gset_eq:cc { xpg@ifdefined } { xpg_if_language_defined:nTF } % Set \bbl@hyphendata@\the\language, which is (lua)babel's % hyphenation pattern hook % FIXME Clarifiy why/when this is needed. \cs_new:Nn \xpg_set_bbl_hyphendata:n { \sys_if_engine_luatex:T { \cs_if_exist:cF {bbl@hyphendata@#1} { \cs_gset:cpn {bbl@hyphendata@\the\language} {} } } } % Set hyphenation patterns for a given language. This does the right % thing both for XeTeX and LuaTeX \cs_new:Nn \xpg_set_hyphenation_patterns:n { \sys_if_engine_luatex:T { \__xpg_luatex_load_lang:n {#1} } \language=\csname l@#1\endcsname } \cs_new:Nn \__xpg_start_language:nn { % hook for compatibility with biblatex \select@language { #2 } \xpg_set_bbl_hyphendata:n {\the\language} \xpg_initial_setup: \xpg_select_language:nn { #1 } { #2 } % Hook for external packages such as biblatex \polyglossia@language@switched \__xpg_lang_set_par_direction:n {#2} \use:c {captions#2} \use:c {date#2} % These are hooks for external packages which want to access variants % via babelname (such as biblatex) \cs_if_exist_use:c {captions@bbl@\babelname} \cs_if_exist_use:c {date@bbl@\babelname} \__xpg_local_marks:n {#2} \use:c {init@extras@#2} \__xpg_indent_first:n { #2 } \cs_if_exist_use:c {blockextras@#2} % This is a hook for external packages which want to access variants % via babelname (such as biblatex) \cs_if_exist_use:c {blockextras@bbl@\babelname} } % hook for compatibility with biblatex % (probably no longer used due to the % more general hook that follows, but % we keep it for backwards comp.) \cs_set:Npn \select@language #1 {} % Hook for external packages such as biblatex % do not rename! \cs_new:Npn \polyglossia@language@switched {} % remove all customization for language #1 \cs_new:Npn \noextrascurrent #1 { \cs_if_exist_use:c{noextras@#1}% % This is a hook for external packages which want to access variants % via babelname (such as biblatex) \cs_if_exist_use:c{noextras@bbl@\babelname} } % Common code for `\select@language' and `\foreignlanguage'. \cs_new:Nn \xpg_select_language:nn { % disable the extras and number settings of the previous language \cs_if_exist:cT{languagename} { \noextrascurrent{\languagename} \cs_if_exist_use:c{no\languagename @numbers} \sys_if_engine_xetex:T{ \__xpg_if_LR_str:eTF{\prop_item:Ne{\l_xpg_langsetup_prop}{\languagename/direction}} { \__xpg_if_LR_str:eF{\prop_item:Nn{\l_xpg_langsetup_prop}{#2/direction}} {\setnonlatin} % LTR -> RTL } { \__xpg_if_LR_str:eT{\prop_item:Nn{\l_xpg_langsetup_prop}{#2/direction}} {\setlatin} % RTL -> LTR } } } \xpg_set_language_name:n { #2 } \xpg_set_normalfont:n { #2 } \xpg_select_fontfamily:n { #2 } \__xpg_use_or_warn:c{#2@language} \cs_if_exist_use:c{#2@numbers} \__xpg_use_localhyphenmins:nn { #1 } { #2 } \__xpg_french_spacing:n { #2 } } \cs_undefine:N \xpg_pop_language: \DeclareDocumentEnvironment { otherlanguage } { O{} m } { \selectlanguage[#1]{#2} } { } % Alias to {}, but more suitable % for specific (esp. tag-based) aliases % where {} would cause clashes % (e.g., \fi) \DeclareEnvironmentCopy { lang } { otherlanguage } \NewDocumentCommand \setlocalhyphenmins { m m m } { \xpg_if_language_defined:nTF{#1} { \cs_if_eq:ccTF { l@#1 } { l@nohyphenation } { \xpg_warning_msg:n {\string\setlocalhyphenmin\space~ useless~ for~ unhyphenated~ language~ #1} }{ \providehyphenmins{#1}{#2#3} } }{ \xpg_warning_msg:n {\string\setlocalhyphenmin\space~ useless~ for~ unknown~ language~ #1} } } % \setlanghyphenmins[options]{lang}{l}{r} \NewDocumentCommand \setlanghyphenmins { O{} m m m } { % Check for real language name and options \tl_set:Nx \l_tmp_opts_tl { \xpg_alias_add_to_option_i:nn{#2}{#1} } \tl_set:Nx \l_tmp_lang_tl { \xpg_alias_base_lang:n{#2} } \c_group_begin_token \xpg_error_if_lang_not_loaded:n{\l_tmp_lang_tl} \SetGlossOptions{\l_tmp_lang_tl}{ \l_tmp_opts_tl } % Store bcp47.tag@hypenmins \cs_set_nopar:cpe {tmp@bcp47.tag} { \prop_item:Ne{\l_xpg_langsetup_prop}{ \l_tmp_lang_tl / bcp47 } } \cs_gset:cpn {\csname tmp@bcp47.tag\endcsname @hyphenmins} {{#3}{#4}} \c_group_end_token } % \__xpg_use_localhyphenmins:nn {options}{lang} \cs_new_nopar:Nn \__xpg_use_localhyphenmins:nn { \c_group_begin_token \xpg_error_if_lang_not_loaded:n {#2} \SetGlossOptions {#2} {#1} % Use bcp47.tag@hypenmins \cs_gset_nopar:cpe {tmp@bcp47.tag} { \prop_item:Nn{\l_xpg_langsetup_prop}{ #2 / bcp47 } } \c_group_end_token \cs_if_exist:cTF {\csname tmp@bcp47.tag\endcsname @hyphenmins} { \tl_set:Ne \l_tmpa_tl { \use:c{\csname tmp@bcp47.tag\endcsname @hyphenmins} } \expandafter \set@hyphenmins \l_tmpa_tl }{ \cs_if_exist:cT{#2hyphenmins} { \expandafter\expandafter\expandafter\set@hyphenmins\csname #2hyphenmins\endcsname\relax } } \sys_if_engine_luatex:T { % Set \totalhyphenmin if specified \prop_get:NeNT \l_xpg_langsetup_prop {#2/totalhyphenmin} \l_tmpb_tl { \xpg_info_msg:n {totalhyphenmin: '\l_tmpb_tl'} \expandafter\hyphenationmin \l_tmpb_tl } } } % Babel previously compiled in hyphenrules into the kernel (via hyphen.cfg) % but this is no longer the case. In any case, we roll our own one now % and possibly overwrite babel's. % As opposed to the one inherited from switch.def/babel, our environment % supports language options and aliases. \DeclareDocumentEnvironment { hyphenrules } { O{} m } { % Check for real language name and options \tl_set:Nx \l_tmp_opts_tl { \xpg_alias_add_to_option_i:nn{#2}{#1} } \tl_set:Nx \l_tmp_lang_tl { \xpg_alias_base_lang:n{#2} } % Register the language options \__xpg_set_language_options:nn { \l_tmp_lang_tl } { \l_tmp_opts_tl } % Now switch patterns \__xpg_use_or_warn:c{\use:c{l_tmp_lang_tl}@language} % And activate hyphenmins \__xpg_use_localhyphenmins:nn { \l_tmp_opts_tl } { \l_tmp_lang_tl } } { } \hook_gput_code:nnn {begindocument/before} {.} { \IfPackageLoadedTF{bidi} { \ProvideDocumentCommand \aemph { m } { $\overline{\hboxR{#1}}$ } }{} \IfPackageLoadedTF{luabidi} { \ProvideDocumentCommand \aemph { m } { $\overline{\hbox{\RL{#1}}}$ } }{} } % keys for main package \keys_define:nn { polyglossia } { verbose .bool_set:N = \g_xpg_verbose_bool, verbose .default:n = true, % compatibility quiet .meta:n = { verbose = false }, localmarks .bool_set:N = \g_xpg_localmarks_bool, localmarks .default:n = true, % compatibility nolocalmarks .meta:n = { localmarks = false }, babelshorthands .legacy_if_set:n = system@babelshorthands, % compatibility babelshorthands .default:n = true, luatexrenderer .str_set:N = \g_xpg_luatex_renderer_str, luatexrenderer .value_required:n = true, } \keys_set:nn { polyglossia } { localmarks = false, verbose = true, babelshorthands = false, luatexrenderer = Harfbuzz } % load by default latex \setmainlanguage{latex} % then process key in order to overwrite \ProcessKeyOptions[polyglossia] % Set the LuaTeX renderer. As opposed to fontspec, we use Harfbuzz by default. % This can be changed via the luatexrenderer package option. \sys_if_engine_luatex:T{ \str_if_eq:eeF{\g_xpg_luatex_renderer_str}{none} { \xpg_info_msg:n{Setting~ LuaTeX~ font~ renderer~ to~ \g_xpg_luatex_renderer_str} \exp_args:Ne \defaultfontfeatures{Renderer=\g_xpg_luatex_renderer_str} } } \bool_if:nF \g_xpg_verbose_bool { \cs_gset_nopar:Npn \@latex@info #1 { \relax } % no latex info \cs_gset_nopar:Npn \@font@info #1 { \relax } % no latex font info \cs_gset_nopar:Npn \@font@warning #1 { \relax } % no latex font warnings \msg_redirect_module:nnn { fontspec } { info } { none } % no fontspec info \msg_redirect_module:nnn { polyglossia } { info } { none } % no polyglossia info } \bool_if:nT \g_xpg_localmarks_bool { \__xpg_enable_local_marks: } % common code to initiate babelshordhands in glosses \cs_new:Npn \InitializeBabelShorthands { \cs_if_exist:cF {initiate@active@char} { \file_input:n {babelsh.def} \initiate@active@char{"} \shorthandoff{"} } } % Control shorthand (de-)activation % This checks that the shorthand char is only deactivated % if we have activated it ourselves and hence keeps % activation of other packages if no shorthands are used. \seq_new:N \g__xpg_active_shorthands_seq \DeclareDocumentCommand \xpg@activate@shorthands { O{"} } { \seq_if_in:NnF \g__xpg_active_shorthands_seq { #1 } { \bbl@activate{#1} \seq_gpush:Nn \g__xpg_active_shorthands_seq { #1 } } } \DeclareDocumentCommand \xpg@deactivate@shorthands { O{"} } { \seq_if_in:NnT \g__xpg_active_shorthands_seq { #1 } { \cs_if_exist:cT{initiate@active@char}{\bbl@deactivate{#1}} \seq_remove_all:Nn \g__xpg_active_shorthands_seq {#1} } } % Inherit shorthands in other languages \NewDocumentCommand \inheritbabelshorthands { m m } { \hook_gput_code:nnn {begindocument/before} {.} { % Load the involved languages if necessary % Error if they do not exist \tl_set:Nn \l__xpg_tmpa_lang_tl { \xpg_alias_base_lang:n{#1} } \clist_if_in:NeF \xpg@loaded {\l__xpg_tmpa_lang_tl}{ \file_if_exist:nTF{gloss-\l__xpg_tmpa_lang_tl .ldf} { \setotherlanguage{#1} } { \xpg_error_msg:n { Source~ language~ #1,~ used~ in~ \string\inheritbabelshorthands,~ does~ not~ exist } } } \tl_set:Nn \l__xpg_tmpb_lang_tl { \xpg_alias_base_lang:n{#2} } \clist_if_in:NeF \xpg@loaded {\l__xpg_tmpb_lang_tl}{ \file_if_exist:nTF{gloss-\l__xpg_tmpb_lang_tl .ldf} { \setotherlanguage{#2} } { \xpg_error_msg:n { Target~ language~ #2,~ used~ in~ \string\inheritbabelshorthands,~ does~ not~ exist } } } % Test whether the requested shorthands exist \bool_if_exist:NF \l__xpg_no_shorthands_bool { \bool_new:N \l__xpg_no_shorthands_bool } \cs_if_exist:cF { \l__xpg_tmpa_lang_tl @shorthands } { \bool_set_true:N \l__xpg_no_shorthands_bool } \cs_if_exist:cF { no\l__xpg_tmpa_lang_tl @shorthands } { \bool_set_true:N \l__xpg_no_shorthands_bool } \bool_if:nT { \l__xpg_no_shorthands_bool } { \xpg_error_msg:n { No~ babel~ shorthands~ exist~ for~ language~ #1 } } % If so, apply: \bool_if:nF { \l__xpg_no_shorthands_bool } { \exp_args:Ncc \addto { blockextras@\l__xpg_tmpb_lang_tl } { \l__xpg_tmpa_lang_tl @shorthands } \exp_args:Ncc \addto { inlineextras@\l__xpg_tmpb_lang_tl } { \l__xpg_tmpa_lang_tl @shorthands } \exp_args:Ncc \addto { noextras@\l__xpg_tmpb_lang_tl } { no\l__xpg_tmpa_lang_tl @shorthands } } } } % Activate shorthands of a (loaded) language inline \NewDocumentCommand \usebabelshorthands { m } { \str_if_eq:nnTF { #1 } { none } { % "none" deactivates any shorthands \languageshorthands{none} } { \tl_set:Nn \l__xpg_tmpa_lang_tl { \xpg_alias_base_lang:n{#1} } \iflanguageloaded{\l__xpg_tmpa_lang_tl}{ \use:c{\l__xpg_tmpa_lang_tl @shorthands} }{ \xpg_error_msg:n { Language~ #1,~ requested~ in~ \string\startbabelshorthands,~ is~ not~ loaded } } } } \endinput