关于“crefthe”代码中不正确的expl3语法

关于“crefthe”代码中不正确的expl3语法

由于我对 还很陌生expl3,我用它编写的第一个包crefthe有很多不正确的语法。感谢 Joseph Wright 的回答和@egreg的回答,我设法修正了一些,但肯定还有更多。当前版本列在最后。

以下是 @egreg 指出的两个我无法修复的问题:

  1. 更改\exp_args:Nx \str_case:nn为会\str_case:Vn破坏功能(介词消失),这可能是由于\str_case_e:nn { \languagename }内部造成的\l__crefthe_prep_mode_tl,但我不确定。

  2. 更改\text_lowercase:n\str_foldcase:n也会破坏冠词缩写功能,即使我将其扩展为使用\str_foldcase:x冠词缩写,仍然无法正常工作。(顺便问一下,我可以假设\text_lowercase:nshall 始终扩展其参数吗,或者最好创建一个:x变体并使用它来确定?)

expl3如果您发现任何不正确的使用方法,或者您有更好的实现方法,请随时告诉我。提前谢谢您!


以下是测试文档:

\documentclass{article}

\usepackage{amsthm}
\usepackage{hyperref}
\usepackage[nameinlink,french]{crefthe}

\usepackage[french]{babel}

\newtheorem{thm}{Théorème}
\newtheorem{thmn}{Théorème facile}

\crefthename{thm}[le]{théorème}[les]{théorèmes}
\crefthename{thmn}[le]{théorème facile}[les]{théorèmes faciles}

\Crefthename{thm}[Le]{théorème}[Les]{théorèmes}
\Crefthename{thmn}[Le]{théorème facile}[Les]{théorèmes faciles}


\begin{document}

\begin{thm}\label{thm1}
    some text.
\end{thm}

\begin{thm}\label{thm2}
    more text.
\end{thm}

\begin{thmn}\label{thmn1}
    more other text.
\end{thmn}


- Single one -

\crefthe[\`A]{thm1}, \crefthe[\`a]{thm1}

\crefthe[À]{thm1}, \crefthe[à]{thm1}

\Crefthe[à]{thm1}, \crefthe[à]{thm1}

\Crefthe[À]{thm1}, \crefthe[à]{thm1}

\crefthe[de]{thm1}

\Crefthe[de]{thm1}


- Multiple ones -

\crefthe[À]{thm1,thmn1}

\Crefthe[À]{thm1,thmn1}

\crefthe[À]{thm1,thm2}

\Crefthe[À]{thm1,thm2}

\Crefthe[De]{thm1,thm2,thmn1}

\Crefthe{thm1,thm2,thmn1}

\crefthe{thm1,thm2}


\end{document}

正确的结果应该是:

在此处输入图片描述

当前版本 (2022-02-09) crefthe

\NeedsTeXFormat{LaTeX2e}[2020-10-01]
\RequirePackage{l3keys2e}
\ProvidesExplPackage
  {crefthe}
  {2022/02/09} {}
  {Cross referencing with proper definite articles}

\keys_define:nn { crefthe }
  {
    , overwrite .bool_set:N = \l__crefthe_overwrite_bool
    , overwrite .initial:n  = { false }
    , unknown .code:n       =
        {
          \PassOptionsToPackage { \CurrentOption } { cleveref }
        }
  }
\ProcessKeysOptions { crefthe }

\RequirePackage { cleveref }

\cs_set_eq:cc { crefthe_cref_original:w }     { cref }
\cs_set_eq:cc { crefthe_Cref_original:w }     { Cref }
\cs_set_eq:cc { crefthe_crefname_original:w } { crefname }
\cs_set_eq:cc { crefthe_Crefname_original:w } { Crefname }

\bool_if:NT \l__crefthe_overwrite_bool
  {
    \hook_gput_next_code:nn { package/crefthe/after }
      {
        % \RenewCommandCopy \cref     \crefthe
        % \RenewCommandCopy \Cref     \Crefthe
        % \RenewCommandCopy \crefname \crefthename
        % \RenewCommandCopy \Crefname \Crefthename
        \cs_set_eq:cc { cref }     { crefthe }
        \cs_set_eq:cc { Cref }     { Crefthe }
        \cs_set_eq:cc { crefname } { crefthename }
        \cs_set_eq:cc { Crefname } { Crefthename }
      }
  }

\tl_new:N \g__crefthe_prep_once_tl
\tl_new:N \g__crefthe_prep_each_tl

\tl_new:N \l__crefthe_prep_mode_tl

\bool_new:N \g__crefthe_uppercase_bool

\NewDocumentCommand \crefthe { s t- t+ O{} m }
  {
    \bool_set_false:N \g__crefthe_uppercase_bool
    \bool_if:nTF { #2 }
      {
        \tl_gset:Nn \g__crefthe_prep_once_tl { #4 }
      }
      {
        \bool_if:nTF { #3 }
          {
            \tl_gset:Nn \g__crefthe_prep_each_tl { #4 }
          }
          {
            \exp_args:Nx \str_case:nn \l__crefthe_prep_mode_tl
              {
                { - } { \tl_gset:Nn \g__crefthe_prep_once_tl { #4 } }
                { + } { \tl_gset:Nn \g__crefthe_prep_each_tl  { #4 } }
              }
          }
      }
    \bool_if:nTF { #1 } { \crefthe_cref_original:w * { #5 } } { \crefthe_cref_original:w { #5 } }
    \tl_gset:Nn \g__crefthe_prep_each_tl {}
  }

\NewDocumentCommand \Crefthe { s t- t+ O{} m }
  {
    \bool_set_true:N \g__crefthe_uppercase_bool
    \bool_if:nTF { #2 }
      {
        \tl_gset:Nn \g__crefthe_prep_once_tl { #4 }
      }
      {
        \bool_if:nTF { #3 }
          {
            \tl_gset:Nn \g__crefthe_prep_each_tl { #4 }
          }
          {
            \exp_args:Nx \str_case:nn \l__crefthe_prep_mode_tl
              {
                { - } { \tl_gset:Nn \g__crefthe_prep_once_tl { #4 } }
                { + } { \tl_gset:Nn \g__crefthe_prep_each_tl  { #4 } }
              }
          }
      }
    \bool_if:nTF { #1 } { \crefthe_Cref_original:w * { #5 } } { \crefthe_Cref_original:w { #5 } }
    \tl_gset:Nn \g__crefthe_prep_each_tl {}
  }

\NewDocumentCommand \crefthename { m O{} m O{} m }
  {
    \tl_if_blank:eTF { #2 }
      {
        \crefthe_crefname_original:w { #1 } { #3 } { #5 }
        \cs_set:cn { cref_#1_format:nnn } { ##2 #3 ~ ##1 ##3 }
        \cs_set:cn { cref_#1_format_first:nnn } { ##2 #5 ~ ##1 ##3 }
      }
      {
        \crefthe_crefname_original:w { #1 } { \crefthemark{ #2 } #3 } { \crefthemark{ #4 } #5 }
        \cs_set:cn { cref_#1_format:nnn } { \crefthemark { #2 } ##2 #3 ~ ##1 ##3 }
        \cs_set:cn { cref_#1_format_first:nnn } { \crefthemark { #4 } ##2 #5 ~ ##1 ##3 }
      }
    \hook_gput_code:nnn { begindocument } { crefthe }
      {
        \cs_set_eq:cc { cref@ #1 @format } { cref_#1_format:nnn }
        \cs_set_eq:cc { cref@ #1 @format@first } { cref_#1_format_first:nnn }
      }
  }

\NewDocumentCommand \Crefthename { m O{} m O{} m }
  {
    \tl_if_blank:eTF { #2 }
      {
        \crefthe_Crefname_original:w { #1 } { #3 } { #5 }
        \cs_set:cn { Cref_#1_format:nnn } { ##2 #3 ~ ##1 ##3 }
        \cs_set:cn { Cref_#1_format_first:nnn } { ##2 #5 ~ ##1 ##3 }
      }
      {
        \crefthe_Crefname_original:w { #1 } { \crefthemark{ #2 } #3 }{ \crefthemark{ #4 } #5 }
        \cs_set:cn { Cref_#1_format:nnn } { \crefthemark { #2 } ##2 #3 ~ ##1 ##3 }
        \cs_set:cn { Cref_#1_format_first:nnn } { \crefthemark { #4 } ##2 #5 ~ ##1 ##3 }
      }
    \hook_gput_code:nnn { begindocument } { crefthe }
      {
        \cs_set_eq:cc { Cref@ #1 @format } { Cref_#1_format:nnn }
        \cs_set_eq:cc { Cref@ #1 @format@first } { Cref_#1_format_first:nnn }
      }
  }

\NewDocumentCommand \crefthemark { m }
  {
    \crefthe_contraction:nn { \g__crefthe_prep_each_tl }
      {
        \crefthe_contraction:nn { \g__crefthe_prep_once_tl } { #1 }
      }
    \tl_gset:Nn \g__crefthe_prep_once_tl {}
    \tl_gset:Nx \g__crefthe_prep_each_tl { \text_lowercase:n \g__crefthe_prep_each_tl }
    \str_if_eq:eeF { \str_tail:n { #1 } } { ' } { ~ }
    \bool_set_false:N \g__crefthe_uppercase_bool
  }

\prg_generate_conditional_variant:Nnn \str_case_e:nn { nv } { p, T, F, TF }

\tl_set:Nn \l__crefthe_prep_mode_tl {
  \str_case_e:nn { \languagename }
    {
      {french}        { + }
      {italian}       { + }
      {spanish}       { - }
      {portuguese}    { + }
      {brazilian}     { + }
    }
}

\cs_new:Npn \crefthe_contraction:nn #1#2
  {
    \tl_if_blank:eTF { #1 }
      { #2 }
      {
        \tl_if_exist:cTF { g_crefthe_contraction_rule_ \languagename _tl }
          {
            \bool_if:NTF \g__crefthe_uppercase_bool
              {
                \str_case_e:nvF { #1 ~ \text_lowercase:n { #2 } }
                { g_crefthe_contraction_rule_uppercase_ \languagename _tl }
                { #1 ~ \text_lowercase:n { #2 } }
              }
              {
                \str_case_e:nvF { #1 ~ \text_lowercase:n { #2 } }
                { g_crefthe_contraction_rule_ \languagename _tl }
                { #1 ~ \text_lowercase:n { #2 } }
              }
          }
          { #1 ~ #2 }
      }
  }

\tl_gset:Nn \g_crefthe_contraction_rule_french_tl
  {
    { à~le }      { au }
    { à~les }     { aux }
    { de~le }     { du }
    { de~les }    { des }
    { À~le }      { Au }
    { À~les }     { Aux }
    { De~le }     { Du }
    { De~les }    { Des }
  }
\tl_gset:Nn \g_crefthe_contraction_rule_uppercase_french_tl
  {
    { à~le }      { Au }
    { à~les }     { Aux }
    { de~le }     { Du }
    { de~les }    { Des }
    { À~le }      { Au }
    { À~les }     { Aux }
    { De~le }     { Du }
    { De~les }    { Des }
  }

\tl_gset:Nn \g_crefthe_contraction_rule_italian_tl
  {
    { a~il }      { al }
    { a~lo }      { allo }
    { a~l' }      { all' }
    { a~la }      { alla }
    { di~il }     { del }
    { di~lo }     { dello }
    { di~l' }     { dell' }
    { di~la }     { della }
    { da~il }     { dal }
    { da~lo }     { dallo }
    { da~l' }     { dall' }
    { da~la }     { dalla }
    { in~il }     { nel }
    { in~lo }     { nello }
    { in~l' }     { nell' }
    { in~la }     { nella }
    { su~il }     { sul }
    { su~lo }     { sullo }
    { su~l' }     { sull' }
    { su~la }     { sulla }
    { a~i }       { ai }
    { a~gli }     { agli }
    { a~le }      { alle }
    { di~i }      { dei }
    { di~gli }    { degli }
    { di~le }     { delle }
    { da~i }      { dai }
    { da~gli }    { dagli }
    { da~le }     { dalle }
    { in~i }      { nei }
    { in~gli }    { negli }
    { in~le }     { nelle }
    { su~i }      { sui }
    { su~gli }    { sugli }
    { su~le }     { sulle }
    { A~il }      { Al }
    { A~lo }      { Allo }
    { A~l' }      { All' }
    { A~la }      { Alla }
    { Di~il }     { Del }
    { Di~lo }     { Dello }
    { Di~l' }     { Dell' }
    { Di~la }     { Della }
    { Da~il }     { Dal }
    { Da~lo }     { Dallo }
    { Da~l' }     { Dall' }
    { Da~la }     { Dalla }
    { In~il }     { Nel }
    { In~lo }     { Nello }
    { In~l' }     { Nell' }
    { In~la }     { Nella }
    { Su~il }     { Sul }
    { Su~lo }     { Sullo }
    { Su~l' }     { Sull' }
    { Su~la }     { Sulla }
    { A~i }       { Ai }
    { A~gli }     { Agli }
    { A~le }      { Alle }
    { Di~i }      { Dei }
    { Di~gli }    { Degli }
    { Di~le }     { Delle }
    { Da~i }      { Dai }
    { Da~gli }    { Dagli }
    { Da~le }     { Dalle }
    { In~i }      { Nei }
    { In~gli }    { Negli }
    { In~le }     { Nelle }
    { Su~i }      { Sui }
    { Su~gli }    { Sugli }
    { Su~le }     { Sulle }
  }
\tl_gset:Nn \g_crefthe_contraction_rule_uppercase_italian_tl
  {
    { a~il }      { Al }
    { a~lo }      { Allo }
    { a~l' }      { All' }
    { a~la }      { Alla }
    { di~il }     { Del }
    { di~lo }     { Dello }
    { di~l' }     { Dell' }
    { di~la }     { Della }
    { da~il }     { Dal }
    { da~lo }     { Dallo }
    { da~l' }     { Dall' }
    { da~la }     { Dalla }
    { in~il }     { Nel }
    { in~lo }     { Nello }
    { in~l' }     { Nell' }
    { in~la }     { Nella }
    { su~il }     { Sul }
    { su~lo }     { Sullo }
    { su~l' }     { Sull' }
    { su~la }     { Sulla }
    { a~i }       { Ai }
    { a~gli }     { Agli }
    { a~le }      { Alle }
    { di~i }      { Dei }
    { di~gli }    { Degli }
    { di~le }     { Delle }
    { da~i }      { Dai }
    { da~gli }    { Dagli }
    { da~le }     { Dalle }
    { in~i }      { Nei }
    { in~gli }    { Negli }
    { in~le }     { Nelle }
    { su~i }      { Sui }
    { su~gli }    { Sugli }
    { su~le }     { Sulle }
    { A~il }      { Al }
    { A~lo }      { Allo }
    { A~l' }      { All' }
    { A~la }      { Alla }
    { Di~il }     { Del }
    { Di~lo }     { Dello }
    { Di~l' }     { Dell' }
    { Di~la }     { Della }
    { Da~il }     { Dal }
    { Da~lo }     { Dallo }
    { Da~l' }     { Dall' }
    { Da~la }     { Dalla }
    { In~il }     { Nel }
    { In~lo }     { Nello }
    { In~l' }     { Nell' }
    { In~la }     { Nella }
    { Su~il }     { Sul }
    { Su~lo }     { Sullo }
    { Su~l' }     { Sull' }
    { Su~la }     { Sulla }
    { A~i }       { Ai }
    { A~gli }     { Agli }
    { A~le }      { Alle }
    { Di~i }      { Dei }
    { Di~gli }    { Degli }
    { Di~le }     { Delle }
    { Da~i }      { Dai }
    { Da~gli }    { Dagli }
    { Da~le }     { Dalle }
    { In~i }      { Nei }
    { In~gli }    { Negli }
    { In~le }     { Nelle }
    { Su~i }      { Sui }
    { Su~gli }    { Sugli }
    { Su~le }     { Sulle }
  }

\tl_gset:Nn \g_crefthe_contraction_rule_spanish_tl
  {
    { a~el }      { al }
    { de~el }     { del }
    { A~el }      { Al }
    { De~el }     { Del }
  }
\tl_gset:Nn \g_crefthe_contraction_rule_uppercase_spanish_tl
  {
    { a~el }      { Al }
    { de~el }     { Del }
    { A~el }      { Al }
    { De~el }     { Del }
  }

\tl_gset:Nn \g_crefthe_contraction_rule_portuguese_tl
  {
    { a~o }       { ao }
    { a~a }       { à }
    { a~os }      { aos }
    { a~as }      { às }
    { de~o }      { do }
    { de~a }      { da }
    { de~os }     { dos }
    { de~as }     { das }
    { em~o }      { no }
    { em~a }      { na }
    { em~os }     { nos }
    { em~as }     { nas }
    { A~o }       { Ao }
    { A~a }       { À }
    { A~os }      { Aos }
    { A~as }      { Às }
    { De~o }      { Do }
    { De~a }      { Da }
    { De~os }     { Dos }
    { De~as }     { Das }
    { Em~o }      { No }
    { Em~a }      { Na }
    { Em~os }     { Nos }
    { Em~as }     { Nas }
  }
\tl_gset:Nn \g_crefthe_contraction_rule_uppercase_portuguese_tl
  {
    { a~o }       { Ao }
    { a~a }       { À }
    { a~os }      { Aos }
    { a~as }      { Às }
    { de~o }      { Do }
    { de~a }      { Da }
    { de~os }     { Dos }
    { de~as }     { Das }
    { em~o }      { No }
    { em~a }      { Na }
    { em~os }     { Nos }
    { em~as }     { Nas }
    { A~o }       { Ao }
    { A~a }       { À }
    { A~os }      { Aos }
    { A~as }      { Às }
    { De~o }      { Do }
    { De~a }      { Da }
    { De~os }     { Dos }
    { De~as }     { Das }
    { Em~o }      { No }
    { Em~a }      { Na }
    { Em~os }     { Nos }
    { Em~as }     { Nas }
  }

\tl_gset:Nn \g_crefthe_contraction_rule_brazilian_tl
  {
    { a~o }       { ao }
    { a~a }       { à }
    { a~os }      { aos }
    { a~as }      { às }
    { de~o }      { do }
    { de~a }      { da }
    { de~os }     { dos }
    { de~as }     { das }
    { em~o }      { no }
    { em~a }      { na }
    { em~os }     { nos }
    { em~as }     { nas }
    { A~o }       { Ao }
    { A~a }       { À }
    { A~os }      { Aos }
    { A~as }      { Às }
    { De~o }      { Do }
    { De~a }      { Da }
    { De~os }     { Dos }
    { De~as }     { Das }
    { Em~o }      { No }
    { Em~a }      { Na }
    { Em~os }     { Nos }
    { Em~as }     { Nas }
  }
\tl_gset:Nn \g_crefthe_contraction_rule_uppercase_brazilian_tl
  {
    { a~o }       { Ao }
    { a~a }       { À }
    { a~os }      { Aos }
    { a~as }      { Às }
    { de~o }      { Do }
    { de~a }      { Da }
    { de~os }     { Dos }
    { de~as }     { Das }
    { em~o }      { No }
    { em~a }      { Na }
    { em~os }     { Nos }
    { em~as }     { Nas }
    { A~o }       { Ao }
    { A~a }       { À }
    { A~os }      { Aos }
    { A~as }      { Às }
    { De~o }      { Do }
    { De~a }      { Da }
    { De~os }     { Dos }
    { De~as }     { Das }
    { Em~o }      { No }
    { Em~a }      { Na }
    { Em~os }     { Nos }
    { Em~as }     { Nas }
  }
\endinput
%%
%% End of file `crefthe.sty'.

答案1

这是我对您的代码的挑剔(或多或少按照我在源代码中找到的顺序):

  1. 您不能真正调用\cref它,\crefthe_cref_original:n因为它不只接受一个n参数(有一个可选*参数)。...:Nn也是错误的,因为*不是强制性的。\crefthe_cref_original:w会更合适;
  2. 我建议使用\RenewCommandCopyfor overwrite,因为你不是复制一个简单的宏,而是复制组成命令的完整宏堆栈,以及需要一起复制的内部结构等(在这种情况下,不会发生任何糟糕的事情,但是你对于编码风格...:)
  3. 由于overwrite发生在包的末尾,因此无需使用钩子package/crefthe/after,尽管我喜欢广告钩子,但只需将代码放在包的末尾即可;
  4. \tl_gset:Nn <tl var> { }\tl_gclear:N <tl var>;
  5. 您应该使用with (在编写/测试代码时,您可以加载然后执行以捕获此类错误);\bool_gset_false:N\g__crefthe_uppercase_boolexpl3\RequirePackage[enable-debug]{expl3}\debug_on:n { check-declarations, deprecation }
  6. 关于\exp_args:Nx \str_case:nn \l__crefthe_prep_mode_tl,它之所以有效是因为你对其进行了编码,但它在概念上是错误的(两次)。首先,标记列表\l__crefthe_prep_mode_tl不包含expl3意义上的“静态”标记列表,而是一个行动:它会根据 的值扩展为+或,因此扩展其值不起作用,因为它需要多个扩展步骤才能成为“静态”内容。其次,因为你随后在 上使用函数,所以它会获取未完全扩展的标记列表内容并将它们转换为无意义的字符串。这里政治上正确的方法是创建一个函数,然后先完全扩展它,然后-\languagenameV\str_...tl\l__crefthe_prep_mode_tl然后使用返回的字符串。代码中对此进行了注释;
  7. \crefthe\Crefthe除了开头的布尔值和函数之外完全相同\crefthe_[Cc]ref_original:w,因此你可以将它们合并到一个\__crefthe_cref_general:NNNnnN函数中。代码重复通常是邪恶的! 和\crefthename也一样\Crefthename
  8. \text_lowercase:n相当慢,所以如果一个宏使用相同的\text_lowercase:n { <something> }两次,我建议e首先对其进行扩展,然后将所有内容传递给使用已经小写的文本的内部宏(我在中这样做了\crefthe_contraction:nn);
  9. 您必须\tl_new:N <tl var>在执行之前执行\tl_(g)set:Nn <tl var> { ... }。不执行该操作是错误的(理论上)。在对它们\g_crefthe_contraction_rule_<language>_tl执行操作之前,您的代码中未声明所有操作\tl_gset:Nn。但是,这些操作永远不会改变,因此它们可以是常量,因此您可以改为使用以下方式定义它们\tl_const:Nn
  10. \crefthe_contraction:nn你执行 时\tl_if_blank:eTF {#1},但是#1是一个包含文本的变量,对文本进行- 或-expandtl很危险。在将变量传递给 之前,使用 -expansion 将变量扩展为其值,然后使用。此外,如果你提前扩展某个变量,你可以根据需要扩展一次,并避免以后多次进行相同的扩展,从而使你的代码通常更快;exV\crefthe_contraction:nn\tl_if_blank:nTF {#1}
  11. 这里您必须使用\text_lowercase:n而不是\str_foldcase:n因为它\str_foldcase:n不适À用于诸如代码之类的字符串,而不是像重音字符这样的文本字符串;
  12. 您的代码没有任何分组,所以似乎不需要全局分配,所以我把一切都变成了本地的;
  13. 您的代码做了一些有趣的事情,我不确定它(目前)是否可以更改。它设法通过利用以下事实进行比较À le:(\`A le在 pdfTeX 中)前者最终会扩展为后者,但最终两者都会扩展为(相同的)乱码并被发现相等。理想情况下,应该有更好的方法来规范化\`A leÀ le或反过来)并比较两者;
  14. 您可以使用\text_titlecase_first:n将缩写的首字母大写,并避免定义基本上是每个缩写列表的副本。此外,brazilianportuguese列表应该相同,因此您可以只定义一个并进行复制;
  1. \str_case:nn(并且扩展\str_case_e:nn)不能有谓词版本( ),因为它会通过在其中注入任意标记来\str_case_p:nn破坏布尔表达式的解析,因此从生成变体是错误的(并且如果按照第 5 点的建议启用调试,则会引发错误)。 \bool_if:nTF_p\str_case_e:nn

我想就这些了...这是你的代码,完全被毁了:)

\begin{filecontents*}[overwrite]{crefthe.sty}
\NeedsTeXFormat{LaTeX2e}[2020-10-01]
\RequirePackage{l3keys2e}
\ProvidesExplPackage
  {crefthe}
  {2022/02/09} {}
  {Cross referencing with proper definite articles}

\keys_define:nn { crefthe }
  {
    , overwrite .bool_set:N = \l__crefthe_overwrite_bool
    , overwrite .initial:n  = { false }
    , unknown .code:n       =
      { \PassOptionsToPackage { \CurrentOption } { cleveref } }
  }
\ProcessKeysOptions { crefthe }

\RequirePackage { cleveref }

\NewCommandCopy \crefthe_cref_original:w     \cref
\NewCommandCopy \crefthe_Cref_original:w     \Cref
\NewCommandCopy \crefthe_crefname_original:w \crefname
\NewCommandCopy \crefthe_Crefname_original:w \Crefname

\str_new:N \l__crefthe_tmpa_str

\tl_new:N \l__crefthe_prep_once_tl
\tl_new:N \l__crefthe_prep_each_tl

\bool_new:N \l__crefthe_uppercase_bool

\NewDocumentCommand \crefthe { s t- t+ O{} m }
  {
    \bool_set_false:N \l__crefthe_uppercase_bool
    \__crefthe_cref_general:NNNnnN #1 #2 #3 {#4} {#5} \crefthe_cref_original:w
  }
\NewDocumentCommand \Crefthe { s t- t+ O{} m }
  {
    \bool_set_true:N \l__crefthe_uppercase_bool
    \__crefthe_cref_general:NNNnnN #1 #2 #3 {#4} {#5} \crefthe_Cref_original:w
  }
\cs_new_protected:Npn \__crefthe_cref_general:NNNnnN #1 #2 #3 #4 #5 #6
  {
    \bool_if:NTF #2
      { \tl_set:Nn \l__crefthe_prep_once_tl {#4} }
      {
        \bool_if:NTF #3
          { \tl_set:Nn \l__crefthe_prep_each_tl {#4} }
          {
            % \str_set:Nx fully expands \__crefthe_prep_mode: into a
            % string, then \str_case:Vn compares the value of the
            % resulting string:
            \str_set:Nx \l__crefthe_tmpa_str { \__crefthe_prep_mode: }
            \str_case:Vn \l__crefthe_tmpa_str
              {
                { - } { \tl_set:Nn \l__crefthe_prep_once_tl {#4} }
                { + } { \tl_set:Nn \l__crefthe_prep_each_tl  {#4} }
              }
          }
      }
    \bool_if:NTF #1
      { #6 * {#5} } % here #6 is \crefthe_cref_original:w or \crefthe_Cref_original:w
      { #6   {#5} }
    \tl_gclear:N \l__crefthe_prep_each_tl
  }

\cs_new:Npn \__crefthe_prep_mode:
  {
    \str_case:Vn { \languagename }
      {
        {french}        { + }
        {italian}       { + }
        {spanish}       { - }
        {portuguese}    { + }
        {brazilian}     { + }
      }
  }

\NewDocumentCommand \crefthename { m O{} m O{} m }
  {
    \__crefthe_name_general:nnnnnNN {#1} {#2} {#3} {#4} {#5}
      c \crefthe_crefname_original:w
  }
\NewDocumentCommand \Crefthename { m O{} m O{} m }
  {
    \__crefthe_name_general:nnnnnNN {#1} {#2} {#3} {#4} {#5}
      C \crefthe_Crefname_original:w
  }
\cs_new_protected:Npn \__crefthe_name_general:nnnnnNN #1 #2 #3 #4 #5 #6 #7
  {
    % #6 is c or C
    % #7 is \crefthe_crefname_original:w or \crefthe_Crefname_original:w
    \tl_if_blank:nTF {#2}
      {
        #7 {#1} {#3} {#5}
        \cs_set:cn { #6ref_#1_format:nnn } { ##2 #3 ~ ##1 ##3 }
        \cs_set:cn { #6ref_#1_format_first:nnn } { ##2 #5 ~ ##1 ##3 }
      }
      {
        #7 {#1} { \crefthemark {#2} #3 } { \crefthemark {#4} #5 }
        \cs_set:cn { #6ref_#1_format:nnn } { \crefthemark {#2} ##2 #3 ~ ##1 ##3 }
        \cs_set:cn { #6ref_#1_format_first:nnn } { \crefthemark {#4} ##2 #5 ~ ##1 ##3 }
      }
    \AddToHook { begindocument }
      {
        \cs_set_eq:cc { #6ref@ #1 @format } { #6ref_#1_format:nnn }
        \cs_set_eq:cc { #6ref@ #1 @format@first } { #6ref_#1_format_first:nnn }
      }
  }

\cs_generate_variant:Nn \text_lowercase:n { V }
\NewDocumentCommand \crefthemark { m }
  {
    \crefthe_contraction:Ve \l__crefthe_prep_each_tl
      { \crefthe_contraction:Vn \l__crefthe_prep_once_tl {#1} }
    \tl_gclear:N \l__crefthe_prep_once_tl
    \tl_set:Nx \l__crefthe_prep_each_tl
      { \text_lowercase:V \l__crefthe_prep_each_tl }
    \str_if_eq:eeF { \str_tail:n {#1} } { ' } { ~ }
    \bool_set_false:N \l__crefthe_uppercase_bool
  }

\prg_generate_conditional_variant:Nnn \str_case_e:nn { nv } { T, F, TF }
\cs_new:Npn \crefthe_contraction:nn #1#2
  {
    \exp_args:Ne \__crefthe_contraction:nnn
      { \text_lowercase:n {#2} } {#1} {#2}
  }
\cs_generate_variant:Nn \crefthe_contraction:nn { V, Ve }
\cs_new:Npn \eshow:n #1 { \exp_args:No \use_none:n { \ERR <#1> } }
\cs_new:Npn \__crefthe_contraction:nnn #1 #2 #3
  {
    % #1 is \text_lowercase:n {#3}
    \tl_if_blank:nTF {#2}
      {#3}
      {
        \tl_if_exist:cTF { c_crefthe_contraction_rule_ \languagename _tl }
          {
            \exp_args:Ne \__crefthe_contraction_uppercase:n
              {
                % This e-expansion here is wrong, but the code doesn't work
                % without it.  See point 13 in my answer...
                %        vv
                \str_case_e:nvF { #2~#1 }
                  { c_crefthe_contraction_rule_ \languagename _tl }
                  { #2~#1 }
              }
          }
          { #2~#3 }
      }
  }
\cs_new:Npn \__crefthe_contraction_uppercase:n #1
  {
    \bool_if:NTF \l__crefthe_uppercase_bool
      { \text_titlecase_first:n }
      { \use:n }
        {#1}
  }

\bool_if:NT \l__crefthe_overwrite_bool
  {
    \RenewCommandCopy \cref     \crefthe
    \RenewCommandCopy \Cref     \Crefthe
    \RenewCommandCopy \crefname \crefthename
    \RenewCommandCopy \Crefname \Crefthename
  }

\tl_const:Nn \c_crefthe_contraction_rule_french_tl
  {
    { à~le }      { au }
    { à~les }     { aux }
    { de~le }     { du }
    { de~les }    { des }
    { À~le }      { Au }
    { À~les }     { Aux }
    { De~le }     { Du }
    { De~les }    { Des }
  }
\tl_const:Nn \c_crefthe_contraction_rule_italian_tl
  {
    { a~il }      { al }
    { a~lo }      { allo }
    { a~l' }      { all' }
    { a~la }      { alla }
    { di~il }     { del }
    { di~lo }     { dello }
    { di~l' }     { dell' }
    { di~la }     { della }
    { da~il }     { dal }
    { da~lo }     { dallo }
    { da~l' }     { dall' }
    { da~la }     { dalla }
    { in~il }     { nel }
    { in~lo }     { nello }
    { in~l' }     { nell' }
    { in~la }     { nella }
    { su~il }     { sul }
    { su~lo }     { sullo }
    { su~l' }     { sull' }
    { su~la }     { sulla }
    { a~i }       { ai }
    { a~gli }     { agli }
    { a~le }      { alle }
    { di~i }      { dei }
    { di~gli }    { degli }
    { di~le }     { delle }
    { da~i }      { dai }
    { da~gli }    { dagli }
    { da~le }     { dalle }
    { in~i }      { nei }
    { in~gli }    { negli }
    { in~le }     { nelle }
    { su~i }      { sui }
    { su~gli }    { sugli }
    { su~le }     { sulle }
    { A~il }      { Al }
    { A~lo }      { Allo }
    { A~l' }      { All' }
    { A~la }      { Alla }
    { Di~il }     { Del }
    { Di~lo }     { Dello }
    { Di~l' }     { Dell' }
    { Di~la }     { Della }
    { Da~il }     { Dal }
    { Da~lo }     { Dallo }
    { Da~l' }     { Dall' }
    { Da~la }     { Dalla }
    { In~il }     { Nel }
    { In~lo }     { Nello }
    { In~l' }     { Nell' }
    { In~la }     { Nella }
    { Su~il }     { Sul }
    { Su~lo }     { Sullo }
    { Su~l' }     { Sull' }
    { Su~la }     { Sulla }
    { A~i }       { Ai }
    { A~gli }     { Agli }
    { A~le }      { Alle }
    { Di~i }      { Dei }
    { Di~gli }    { Degli }
    { Di~le }     { Delle }
    { Da~i }      { Dai }
    { Da~gli }    { Dagli }
    { Da~le }     { Dalle }
    { In~i }      { Nei }
    { In~gli }    { Negli }
    { In~le }     { Nelle }
    { Su~i }      { Sui }
    { Su~gli }    { Sugli }
    { Su~le }     { Sulle }
  }
\tl_const:Nn \c_crefthe_contraction_rule_spanish_tl
  {
    { a~el }      { al }
    { de~el }     { del }
    { A~el }      { Al }
    { De~el }     { Del }
  }
\tl_const:Nn \c_crefthe_contraction_rule_portuguese_tl
  {
    { a~o }       { ao }
    { a~a }       { à }
    { a~os }      { aos }
    { a~as }      { às }
    { de~o }      { do }
    { de~a }      { da }
    { de~os }     { dos }
    { de~as }     { das }
    { em~o }      { no }
    { em~a }      { na }
    { em~os }     { nos }
    { em~as }     { nas }
    { A~o }       { Ao }
    { A~a }       { À }
    { A~os }      { Aos }
    { A~as }      { Às }
    { De~o }      { Do }
    { De~a }      { Da }
    { De~os }     { Dos }
    { De~as }     { Das }
    { Em~o }      { No }
    { Em~a }      { Na }
    { Em~os }     { Nos }
    { Em~as }     { Nas }
  }
\tl_const:Nx \c_crefthe_contraction_rule_brazilian_tl
  { \exp_not:V \c_crefthe_contraction_rule_portuguese_tl }
\endinput
%%
%% End of file `crefthe.sty'.
\end{filecontents*}
\documentclass{article}

\usepackage{amsthm}
\usepackage{hyperref}
\usepackage[nameinlink,french]{crefthe}

\usepackage[french]{babel}

\newtheorem{thm}{Théorème}
\newtheorem{thmn}{Théorème facile}

\crefthename{thm}[le]{théorème}[les]{théorèmes}
\crefthename{thmn}[le]{théorème facile}[les]{théorèmes faciles}

\Crefthename{thm}[Le]{théorème}[Les]{théorèmes}
\Crefthename{thmn}[Le]{théorème facile}[Les]{théorèmes faciles}


\begin{document}

\begin{thm}\label{thm1}
    some text.
\end{thm}

\begin{thm}\label{thm2}
    more text.
\end{thm}

\begin{thmn}\label{thmn1}
    more other text.
\end{thmn}


- Single one -

\crefthe[\`A]{thm1}, \crefthe[\`a]{thm1}

\crefthe[À]{thm1}, \crefthe[à]{thm1}

\Crefthe[à]{thm1}, \crefthe[à]{thm1}

\Crefthe[À]{thm1}, \crefthe[à]{thm1}

\crefthe[de]{thm1}

\Crefthe[de]{thm1}


- Multiple ones -

\crefthe[À]{thm1,thmn1}

\Crefthe[À]{thm1,thmn1}

\crefthe[À]{thm1,thm2}

\Crefthe[À]{thm1,thm2}

\Crefthe[De]{thm1,thm2,thmn1}

\Crefthe{thm1,thm2,thmn1}

\crefthe{thm1,thm2}


\end{document}

答案2

作为参考,以下是当前(截至 2022-02-10)的更正版本:

\NeedsTeXFormat{LaTeX2e}[2020-10-01]
\RequirePackage{l3keys2e}
\ProvidesExplPackage
  {crefthe}
  {2022/02/10} {}
  {Cross referencing with proper definite articles}

\keys_define:nn { crefthe }
  {
    , overwrite .bool_set:N = \l__crefthe_overwrite_bool
    , overwrite .initial:n  = { false }
    , unknown .code:n       =
      { \PassOptionsToPackage { \CurrentOption } { cleveref } }
  }
\ProcessKeysOptions { crefthe }

\RequirePackage { cleveref }

\NewCommandCopy \crefthe_cref_original:w     \cref
\NewCommandCopy \crefthe_Cref_original:w     \Cref
\NewCommandCopy \crefthe_crefname_original:w \crefname
\NewCommandCopy \crefthe_Crefname_original:w \Crefname

\str_new:N \l__crefthe_tmpa_str

% \l__crefthe_prep_once_tl is for the "-" mode,
% that only passes the preposition to the first definite article;
% \l__crefthe_prep_each_tl is for the "+" mode,
% that passes the preposition to every definite article.
\tl_new:N \l__crefthe_prep_once_tl
\tl_new:N \l__crefthe_prep_each_tl

\bool_new:N \l__crefthe_uppercase_bool

\NewDocumentCommand \crefthe { s t- t+ O{} m }
  {
    \bool_set_false:N \l__crefthe_uppercase_bool
    \__crefthe_cref_general:NNNnnN #1 #2 #3 { #4 } { #5 } \crefthe_cref_original:w
  }
\NewDocumentCommand \Crefthe { s t- t+ O{} m }
  {
    \bool_set_true:N \l__crefthe_uppercase_bool
    \__crefthe_cref_general:NNNnnN #1 #2 #3 { #4 } { #5 } \crefthe_Cref_original:w
  }
\cs_new_protected:Npn \__crefthe_cref_general:NNNnnN #1 #2 #3 #4 #5 #6
  {
    \bool_if:NTF #2
      { \tl_set:Nn \l__crefthe_prep_once_tl { #4 } }
      {
        \bool_if:NTF #3
          { \tl_set:Nn \l__crefthe_prep_each_tl { #4 } }
          {
            % \str_set:Nx fully expands \__crefthe_prep_mode: into a
            % string, then \str_case:Vn compares the value of the
            % resulting string:
            \str_set:Nx \l__crefthe_tmpa_str { \__crefthe_prep_mode: }
            \str_case:Vn \l__crefthe_tmpa_str
              {
                { - } { \tl_set:Nn \l__crefthe_prep_once_tl { #4 } }
                { + } { \tl_set:Nn \l__crefthe_prep_each_tl  { #4 } }
              }
          }
      }
    \bool_if:NTF #1
      { #6 * { #5 } } % here #6 is \crefthe_cref_original:w or \crefthe_Cref_original:w
      { #6   { #5 } }
    \tl_gclear:N \l__crefthe_prep_each_tl
  }

% \__crefthe_prep_mode: defines the default mode for supported languages
\cs_new:Npn \__crefthe_prep_mode:
  {
    \str_case:Vn \languagename
      {
        {french}        { + }
        {italian}       { + }
        {spanish}       { - }
        {portuguese}    { + }
        {brazilian}     { + }
      }
  }

\NewDocumentCommand \crefthename { m O{} m O{} m }
  {
    \__crefthe_name_general:nnnnnNN { #1 } { #2 } { #3 } { #4 } { #5 }
      c \crefthe_crefname_original:w
  }
\NewDocumentCommand \Crefthename { m O{} m O{} m }
  {
    \__crefthe_name_general:nnnnnNN { #1 } { #2 } { #3 } { #4 } { #5 }
      C \crefthe_Crefname_original:w
  }
\cs_new_protected:Npn \__crefthe_name_general:nnnnnNN #1 #2 #3 #4 #5 #6 #7
  {
    % #6 is c or C
    % #7 is \crefthe_crefname_original:w or \crefthe_Crefname_original:w
    \tl_if_blank:nTF { #2 }
      {
        #7 { #1 } { #3 } { #5 }
        \cs_set:cn { #6ref_#1_format:nnn } { ##2 #3 ~ ##1 ##3 }
        \cs_set:cn { #6ref_#1_format_first:nnn } { ##2 #5 ~ ##1 ##3 }
      }
      {
        #7 { #1 } { \crefthemark { #2 } #3 } { \crefthemark { #4 } #5 }
        \cs_set:cn { #6ref_#1_format:nnn } { \crefthemark { #2 } ##2 #3 ~ ##1 ##3 }
        \cs_set:cn { #6ref_#1_format_first:nnn } { \crefthemark { #4 } ##2 #5 ~ ##1 ##3 }
      }
    \hook_gput_code:nnn { begindocument } { crefthe }
      {
        \cs_set_eq:cc { #6ref@ #1 @format } { #6ref_#1_format:nnn }
        \cs_set_eq:cc { #6ref@ #1 @format@first } { #6ref_#1_format_first:nnn }
      }
  }

\cs_generate_variant:Nn \text_lowercase:n { V }
\NewDocumentCommand \crefthemark { m }
  {
    \crefthe_contraction:Ve \l__crefthe_prep_each_tl
      { \crefthe_contraction:Vn \l__crefthe_prep_once_tl { #1 } }
    \tl_gclear:N \l__crefthe_prep_once_tl
    \tl_set:Nx \l__crefthe_prep_each_tl
      { \text_lowercase:V \l__crefthe_prep_each_tl }
    \str_if_eq:eeF { \str_tail:n { #1 } } { ' } { ~ }
    \bool_set_false:N \l__crefthe_uppercase_bool
  }

\prg_generate_conditional_variant:Nnn \str_case_e:nn { nv } { T, F, TF }
\cs_new:Npn \crefthe_contraction:nn #1#2
  {
    \exp_args:Ne \__crefthe_contraction:nnn
      { \text_lowercase:n { #2 } } { #1 } { #2 }
  }
\cs_generate_variant:Nn \crefthe_contraction:nn { V, Ve }
\cs_new:Npn \__crefthe_contraction:nnn #1 #2 #3
  {
    % #1 is \text_lowercase:n { #3 }
    % #2 is the preposition
    \tl_if_blank:nTF { #2 }
      { #3 }
      {
        \tl_if_exist:cTF { c_crefthe_contraction_rule_ \languagename _tl }
          {
            \exp_args:Ne \__crefthe_conditional_uppercase:n
              {
                \str_case_e:nvF { #2~#1 }
                  { c_crefthe_contraction_rule_ \languagename _tl }
                  { #2~#1 }
              }
          }
          { #2~#3 }
      }
  }
\cs_new:Npn \__crefthe_conditional_uppercase:n #1
  {
    \bool_if:NTF \l__crefthe_uppercase_bool
      { \text_titlecase_first:n }
      { \use:n }
        { #1 }
  }

\hook_gput_code:nnn { begindocument/end } { crefthe }
  {
    \bool_if:NT \l__crefthe_overwrite_bool
      {
        \RenewCommandCopy \cref     \crefthe
        \RenewCommandCopy \Cref     \Crefthe
        \RenewCommandCopy \crefname \crefthename
        \RenewCommandCopy \Crefname \Crefthename
      }
  }

\tl_const:Nn \c_crefthe_contraction_rule_french_tl
  {
    { à~le   }     { au  }
    { à~les  }     { aux }
    { de~le  }     { du  }
    { de~les }     { des }
    { À~le   }     { Au  }
    { À~les  }     { Aux }
    { De~le  }     { Du  }
    { De~les }     { Des }
  }
\tl_const:Nn \c_crefthe_contraction_rule_italian_tl
  {
    { a~il   }     { al    }
    { a~lo   }     { allo  }
    { a~l'   }     { all'  }
    { a~la   }     { alla  }
    { di~il  }     { del   }
    { di~lo  }     { dello }
    { di~l'  }     { dell' }
    { di~la  }     { della }
    { da~il  }     { dal   }
    { da~lo  }     { dallo }
    { da~l'  }     { dall' }
    { da~la  }     { dalla }
    { in~il  }     { nel   }
    { in~lo  }     { nello }
    { in~l'  }     { nell' }
    { in~la  }     { nella }
    { su~il  }     { sul   }
    { su~lo  }     { sullo }
    { su~l'  }     { sull' }
    { su~la  }     { sulla }
    { a~i    }     { ai    }
    { a~gli  }     { agli  }
    { a~le   }     { alle  }
    { di~i   }     { dei   }
    { di~gli }     { degli }
    { di~le  }     { delle }
    { da~i   }     { dai   }
    { da~gli }     { dagli }
    { da~le  }     { dalle }
    { in~i   }     { nei   }
    { in~gli }     { negli }
    { in~le  }     { nelle }
    { su~i   }     { sui   }
    { su~gli }     { sugli }
    { su~le  }     { sulle }
    { A~il   }     { Al    }
    { A~lo   }     { Allo  }
    { A~l'   }     { All'  }
    { A~la   }     { Alla  }
    { Di~il  }     { Del   }
    { Di~lo  }     { Dello }
    { Di~l'  }     { Dell' }
    { Di~la  }     { Della }
    { Da~il  }     { Dal   }
    { Da~lo  }     { Dallo }
    { Da~l'  }     { Dall' }
    { Da~la  }     { Dalla }
    { In~il  }     { Nel   }
    { In~lo  }     { Nello }
    { In~l'  }     { Nell' }
    { In~la  }     { Nella }
    { Su~il  }     { Sul   }
    { Su~lo  }     { Sullo }
    { Su~l'  }     { Sull' }
    { Su~la  }     { Sulla }
    { A~i    }     { Ai    }
    { A~gli  }     { Agli  }
    { A~le   }     { Alle  }
    { Di~i   }     { Dei   }
    { Di~gli }     { Degli }
    { Di~le  }     { Delle }
    { Da~i   }     { Dai   }
    { Da~gli }     { Dagli }
    { Da~le  }     { Dalle }
    { In~i   }     { Nei   }
    { In~gli }     { Negli }
    { In~le  }     { Nelle }
    { Su~i   }     { Sui   }
    { Su~gli }     { Sugli }
    { Su~le  }     { Sulle }
  }
\tl_const:Nn \c_crefthe_contraction_rule_spanish_tl
  {
    { a~el  }      { al  }
    { de~el }      { del }
    { A~el  }      { Al  }
    { De~el }      { Del }
  }
\tl_const:Nn \c_crefthe_contraction_rule_portuguese_tl
  {
    { a~o   }      { ao  }
    { a~a   }      { à   }
    { a~os  }      { aos }
    { a~as  }      { às  }
    { de~o  }      { do  }
    { de~a  }      { da  }
    { de~os }      { dos }
    { de~as }      { das }
    { em~o  }      { no  }
    { em~a  }      { na  }
    { em~os }      { nos }
    { em~as }      { nas }
    { A~o   }      { Ao  }
    { A~a   }      { À   }
    { A~os  }      { Aos }
    { A~as  }      { Às  }
    { De~o  }      { Do  }
    { De~a  }      { Da  }
    { De~os }      { Dos }
    { De~as }      { Das }
    { Em~o  }      { No  }
    { Em~a  }      { Na  }
    { Em~os }      { Nos }
    { Em~as }      { Nas }
  }
\tl_const:Nx \c_crefthe_contraction_rule_brazilian_tl
  { \exp_not:V \c_crefthe_contraction_rule_portuguese_tl }

\endinput
%%
%% End of file `crefthe.sty'.

相关内容