\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 = {<alph_csname>,<Alph_csname>}
%% TODO: localnumeral = <csname>
%%       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[<options>]{<language>}{<alias>}
\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 \<xx>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 \<xx>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<lang>, but more suitable
% for specific (esp. tag-based) aliases
% where \text<alias> 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<lang>
% #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<lang> 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<lang> 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{<lang>}{<key>}{<alias vals, comma-separated>}
\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 {<lang>}, but more suitable
% for specific (esp. tag-based) aliases
% where {<alias>} 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
