0

This was the underlying motivation for a previous question of mine. However, after several hours of failure, I decide to ask this problem directly.

The goal

In order to support the four declensions of German in the package crefthe, I intended to define a new command \crefthevariantname, whose usage would be like:

\crefthevariantname{theorem}
  {
    {Satz}{Sätze}
    , Nominativ = [der]{Satz}[die]{Sätze}
    , Genitiv   = [des]{Satzes}[der]{Sätze}
    , Dativ     = [dem]{Satz}[den]{Sätzen}
    , Akkusativ = [den]{Satz}[die]{Sätze}
  }

Ideally, it should internally convert this input into:

\crefthename{theorem}
  [
    \str_case:NnF \l__crefthe_variant_tl
      {
        { Nominativ } { der }
        { Genitiv   } { des }
        { Dativ     } { dem }
        { Akkusativ } { den }
      } { }
  ]
  {
    \str_case:NnF \l__crefthe_variant_tl
      {
        { Nominativ } { Satz }
        { Genitiv   } { Satzes }
        { Dativ     } { Satz }
        { Akkusativ } { Satz }
      } { Satz }
  }
  [
    \str_case:Nn \l__crefthe_variant_tl
      {
        { Nominativ } { die }
        { Genitiv   } { der }
        { Dativ     } { den }
        { Akkusativ } { die }
      } { }
  ]
  {
    \str_case:Nn \l__crefthe_variant_tl
      {
        { Nominativ } { Sätze }
        { Genitiv   } { Sätze }
        { Dativ     } { Sätzen }
        { Akkusativ } { Sätze }
      } { Sätze }
  }

I've tested the code above directly and it works, so the only thing remains to do is to achieve this transition.

What I have done

For now, I have successfully managed to parse the input, with the code below:

\keys_define:nn { crefthe-variant-parse }
  {
    , unknown .code:n = {
                          \seq_put_right:No \l__crefthe_variant_variant_seq { \l_keys_key_tl }
                          \__crefthe_variant_append_to_seq:w #1
                        }
  }

\bool_new:N \l__crefthe_variant_dafault_given_bool
\cs_new:Nn \crefthe_parse_variant_string:n
  % #1 = clist of configuration
  {
    \bool_set_false:N \l__crefthe_variant_dafault_given_bool
    \clist_map_inline:nn { #1 }
      {
        \str_if_in:nnTF { ##1 } { = }
          {
            \keys_set:nn { crefthe-variant-parse } { ##1 }
          }
          {
            \bool_set_true:N \l__crefthe_variant_dafault_given_bool
            \__crefthe_variant_set_default:w ##1
          }
      }
    \__crefthe_variant_create_prop_from_seq:n { article_singular }
    \__crefthe_variant_create_prop_from_seq:n { article_plural }
    \__crefthe_variant_create_prop_from_seq:n { singular }
    \__crefthe_variant_create_prop_from_seq:n { plural }
  }

\tl_new:N \l__crefthe_variant_default_article_singular_tl
\tl_new:N \l__crefthe_variant_default_article_plural_tl
\tl_new:N \l__crefthe_variant_default_singular_tl
\tl_new:N \l__crefthe_variant_default_plural_tl
\NewDocumentCommand \__crefthe_variant_set_default:w { O{} m O{} m }
  {
    \tl_set:Nn \l__crefthe_variant_default_article_singular_tl { #1 }
    \tl_set:Nn \l__crefthe_variant_default_article_plural_tl { #3 }
    \tl_set:Nn \l__crefthe_variant_default_singular_tl { #2 }
    \tl_set:Nn \l__crefthe_variant_default_plural_tl { #4 }
  }

\seq_new:N \l__crefthe_variant_variant_seq
\seq_new:N \l__crefthe_variant_article_singular_seq
\seq_new:N \l__crefthe_variant_article_plural_seq
\seq_new:N \l__crefthe_variant_singular_seq
\seq_new:N \l__crefthe_variant_plural_seq
\NewDocumentCommand \__crefthe_variant_append_to_seq:w { O{} m O{} m }
  {
    \seq_put_right:Nn \l__crefthe_variant_article_singular_seq { #1 }
    \seq_put_right:Nn \l__crefthe_variant_article_plural_seq { #3 }
    \seq_put_right:Nn \l__crefthe_variant_singular_seq { #2 }
    \seq_put_right:Nn \l__crefthe_variant_plural_seq { #4 }
  }

\cs_new:Nn \__crefthe_variant_create_prop_from_seq:n
  {
    \prop_clear_new:c { l__crefthe_variant_ #1 _prop }
    \int_step_inline:nn { \seq_count:c { l__crefthe_variant_ #1 _seq } }
      {
        \prop_put:cxx { l__crefthe_variant_ #1 _prop }
          { \seq_item:cn { l__crefthe_variant_variant_seq } { ##1 } }
          { \seq_item:cn { l__crefthe_variant_ #1 _seq } { ##1 } }
      }
  }

The main command is \crefthe_parse_variant_string:n. It shall parse the default line (the one without =, in the previous example it is {Satz}{Sätze}) and the other lines (like Nominativ = [der]{Satz}[die]{Sätze}) separately:

  1. \__crefthe_variant_set_default:w parses the default line, saves the strings into the corresponding \l__crefthe_variant_default_<type>_tl;
  2. \__crefthe_variant_append_to_seq:w processes the other lines, at each time appends the string into the corresponding \l__crefthe_variant_<type>_seq.

Moreover, though I'm not sure if it would be helpful in the final solution, with the help of this @egreg's answer, there is a \__crefthe_variant_create_prop_from_seq:n to convert a pair of sequences into a property list.

What I don't know how to do

I would like then to achieve the [...] {...} [...] {...} part in \crefthe_pass_variant_string_to:n and then pass it to \crefthename{<name>} to get the desired result.

However, since all these variables, whether tl or seq, are reusable, when converting into the final result, they have to be properly expanded, i.e. to store their values instead of the macros themselves. However, since I am not experienced at controlling expansion, even with the powerful expl3 I feel hard to correctly achieve this.

My sole purpose is to achieve the main goal, i.e. the translation from the input into something that can be accepted by \crefthename, a command which receives m O{} m O{} m type arguments. Thus if you find my idea tedious or have alternative and maybe better approach, please kindly share your opinion with me.

MWE

Below is a MWE. Since the problem is really about macro calling and expanding, I did not include specific code about crefthe. In some commented part you may see my failed attempts trying to make use of Max Chernoff's and @egreg's code.

\documentclass{article}

\begin{document}

\ExplSyntaxOn

\NewDocumentCommand \crefthevariantname { m m }
  {
    \crefthe_parse_variant_string:n { #2 }
    \crefthe_pass_variant_string_to:n { \crefthename {#1} }
  }

\keys_define:nn { crefthe-variant-parse }
  {
    , unknown .code:n = {
                          \seq_put_right:No \l__crefthe_variant_variant_seq { \l_keys_key_tl }
                          \__crefthe_variant_append_to_seq:w #1
                        }
  }

\bool_new:N \l__crefthe_variant_dafault_given_bool
\cs_new:Nn \crefthe_parse_variant_string:n
  % #1 = clist of configuration
  {
    \bool_set_false:N \l__crefthe_variant_dafault_given_bool
    \clist_map_inline:nn { #1 }
      {
        \str_if_in:nnTF { ##1 } { = }
          {
            \keys_set:nn { crefthe-variant-parse } { ##1 }
          }
          {
            \bool_set_true:N \l__crefthe_variant_dafault_given_bool
            \__crefthe_variant_set_default:w ##1
          }
      }
    \__crefthe_variant_create_prop_from_seq:n { article_singular }
    \__crefthe_variant_create_prop_from_seq:n { article_plural }
    \__crefthe_variant_create_prop_from_seq:n { singular }
    \__crefthe_variant_create_prop_from_seq:n { plural }
  }

\tl_new:N \l__crefthe_variant_default_article_singular_tl
\tl_new:N \l__crefthe_variant_default_article_plural_tl
\tl_new:N \l__crefthe_variant_default_singular_tl
\tl_new:N \l__crefthe_variant_default_plural_tl
\NewDocumentCommand \__crefthe_variant_set_default:w { O{} m O{} m }
  {
    \tl_set:Nn \l__crefthe_variant_default_article_singular_tl { #1 }
    \tl_set:Nn \l__crefthe_variant_default_article_plural_tl { #3 }
    \tl_set:Nn \l__crefthe_variant_default_singular_tl { #2 }
    \tl_set:Nn \l__crefthe_variant_default_plural_tl { #4 }
  }

\seq_new:N \l__crefthe_variant_variant_seq
\seq_new:N \l__crefthe_variant_article_singular_seq
\seq_new:N \l__crefthe_variant_article_plural_seq
\seq_new:N \l__crefthe_variant_singular_seq
\seq_new:N \l__crefthe_variant_plural_seq
\NewDocumentCommand \__crefthe_variant_append_to_seq:w { O{} m O{} m }
  {
    \seq_put_right:Nn \l__crefthe_variant_article_singular_seq { #1 }
    \seq_put_right:Nn \l__crefthe_variant_article_plural_seq { #3 }
    \seq_put_right:Nn \l__crefthe_variant_singular_seq { #2 }
    \seq_put_right:Nn \l__crefthe_variant_plural_seq { #4 }
  }

\cs_new:Nn \__crefthe_variant_create_prop_from_seq:n
  {
    \prop_clear_new:c { l__crefthe_variant_ #1 _prop }
    \int_step_inline:nn { \seq_count:c { l__crefthe_variant_ #1 _seq } }
      {
        \prop_put:cxx { l__crefthe_variant_ #1 _prop }
          { \seq_item:cn { l__crefthe_variant_variant_seq } { ##1 } }
          { \seq_item:cn { l__crefthe_variant_ #1 _seq } { ##1 } }
      }
  }

% \cs_new_protected:Nn \__crefthe_variant_map_dafault:nn
%   {
%     \str_if_eq:onT { \l__crefthe_variant_tl } { #1 }
%       {
%         \prg_break:n { #2 \use_none:n }
%       }
%   }
% \cs_new_protected:Nn \__crefthe_variant_map:nn
%   {
%     \str_if_eq:onT { \l__crefthe_variant_tl } { #1 }
%       {
%         \prg_break:n { #2 }
%       }
%   }
\cs_new:Nn \crefthe_pass_variant_string_to:n
  {
    \bool_if:NTF \l__crefthe_variant_dafault_given_bool
      {
        #1
        % [
        %   \prop_if_in:coTF { l__crefthe_variant_article_singular_prop } { \l__crefthe_variant_tl }
        %     {
        %       \prop_get:coN { l__crefthe_variant_article_singular_prop } { \l__crefthe_variant_tl } \l_tmpa_tl
        %       \tl_use:N \l_tmpa_tl
        %     }
        %     { \l__crefthe_variant_default_article_singular_tl }
        % ]
        % {
        %   \prop_if_in:coTF { l__crefthe_variant_singular_prop } { \l__crefthe_variant_tl }
        %     {
        %       \prop_get:coN { l__crefthe_variant_singular_prop } { \l__crefthe_variant_tl } \l_tmpa_tl
        %       \tl_use:N \l_tmpa_tl
        %     }
        %     { \l__crefthe_variant_default_singular_tl }
        % }
        % [
        %   \prop_if_in:coTF { l__crefthe_variant_article_plural_prop } { \l__crefthe_variant_tl }
        %     {
        %       \prop_get:coN { l__crefthe_variant_article_plural_prop } { \l__crefthe_variant_tl } \l_tmpa_tl
        %       \tl_use:N \l_tmpa_tl
        %     }
        %     { \l__crefthe_variant_default_article_plural_tl }
        % ]
        % {
        %   \prop_if_in:coTF { l__crefthe_variant_plural_prop } { \l__crefthe_variant_tl }
        %     {
        %       \prop_get:coN { l__crefthe_variant_plural_prop } { \l__crefthe_variant_tl } \l_tmpa_tl
        %       \tl_use:N \l_tmpa_tl
        %     }
        %     { \l__crefthe_variant_default_plural_tl }
        % }
      }
      {
        #1
        % [
        %   \seq_map_pairwise_function:NNN
        %     \l__crefthe_variant_variant_seq
        %     \l__crefthe_variant_article_singular_seq
        %     \__crefthe_variant_map:nn
        % ]
        % {
        %   \seq_map_pairwise_function:NNN
        %     \l__crefthe_variant_variant_seq
        %     \l__crefthe_variant_singular_seq
        %     \__crefthe_variant_map:nn
        % }
        % [
        %   \seq_map_pairwise_function:NNN
        %     \l__crefthe_variant_variant_seq
        %     \l__crefthe_variant_article_plural_seq
        %     \__crefthe_variant_map:nn
        % ]
        % {
        %   \seq_map_pairwise_function:NNN
        %     \l__crefthe_variant_variant_seq
        %     \l__crefthe_variant_plural_seq
        %     \__crefthe_variant_map:nn
        % }
      }
  }

\ExplSyntaxOff

Text in case of empty.

\end{document}

Below is a MWE with specific example of crefthe (due to the character count limitation I can only omit some of the code as % ...; you may find the corresponding code in the MWE above, if needed).

\documentclass{article}

\usepackage{amsthm}
\usepackage{crefthe}
\newtheorem{testenv}{Testenv}

\begin{document}

\ExplSyntaxOn

\NewDocumentCommand \crefthevariantname { m m }
  {
    \crefthe_parse_variant_string:n { #2 }
    \crefthe_pass_variant_string_to:n { \crefthename {#1} }
  }

%...

\cs_new:Nn \crefthe_pass_variant_string_to:n
  {
    \bool_if:NTF \l__crefthe_variant_dafault_given_bool
      {
        #1
        % ...
      }
      {
        #1
        % ...
      }
  }

\ExplSyntaxOff

\crefthevariantname{testenv}
  {
    [0]{Zero}[00]{Zero}
    , Nominativ = [1]{One}[11]{Ones}
    , Genitiv   = [2]{Two}[22]{Twos}
    , Dativ     = [3]{Three}[33]{Threes}
    , Akkusativ = [4]{Four}[44]{Fours}
  }

\begin{testenv}\label{testenv:test}
    Environment for testing...
\end{testenv}

\crefthe{testenv:test}

\crefthe[variant=Nominativ]{testenv:test}

\crefthe[variant=Genitiv]{testenv:test}

\crefthe[variant=Dativ]{testenv:test}

\crefthe[variant=Akkusativ]{testenv:test}

\end{document}

The expected result would be 0 Zero <number>, 1 One <number>, 2 Two <number> etc. I didn't include actual preposition or definite article for testing article contraction, but let's hope that the expansion works as expected.


Just in case needed, below is the current (as of 2023/07/20) version of crefthe. It contains the definition of the command option variant and the corresponding macro \l__crefthe_variant_tl.

\NeedsTeXFormat{LaTeX2e}[2022-06-01]
\ProvidesExplPackage
  {crefthe}
  {2023/07/20} {}
  {Cross referencing with proper definite articles}

\keys_define:nn { crefthe }
  {
    , overwrite  .bool_set:N = \l__crefthe_overwrite_bool
    , overwrite  .initial:n  = { false }
    , nameinlink .bool_set:N = \l__crefthe_nameinlink_bool
    , nameinlink .initial:n  = { false }
    , unknown    .code:n     =
        { \PassOptionsToPackage { \CurrentOption } { cleveref } }
  }
\ProcessKeyOptions [ crefthe ]

\bool_if:NT \l__crefthe_nameinlink_bool
  {
    \PassOptionsToPackage { nameinlink } { cleveref }
  }

\RequirePackage { cleveref }
\RequirePackage { regexpatch }

\NewCommandCopy \crefthe_cref_original:w      \cref
\NewCommandCopy \crefthe_Cref_original:w      \Cref
\NewCommandCopy \crefthe_crefname_original:w  \crefname
\NewCommandCopy \crefthe_Crefname_original:w  \Crefname
\NewCommandCopy \crefthe_namecref_original:w  \namecref
\NewCommandCopy \crefthe_nameCref_original:w  \nameCref
\NewCommandCopy \crefthe_namecrefs_original:w \namecrefs
\NewCommandCopy \crefthe_nameCrefs_original:w \nameCrefs

\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 \g__crefthe_uppercase_bool
\bool_new:N \g__crefthe_has_prep_bool

\NewDocumentCommand \crefthe { s t- t+ O{} m }
  {
    \bool_gset_false:N \g__crefthe_uppercase_bool
    \__crefthe_cref_general:NNNnnN #1 #2 #3 { #4 } { #5 } \crefthe_cref_original:w
  }
\NewDocumentCommand \Crefthe { s t- t+ O{} m }
  {
    \bool_gset_true:N \g__crefthe_uppercase_bool
    \__crefthe_cref_general:NNNnnN #1 #2 #3 { #4 } { #5 } \crefthe_Cref_original:w
  }

\NewDocumentCommand \namecrefthe { t- t+ O{} m }
  {
    \bool_gset_false:N \g__crefthe_uppercase_bool
    \__crefthe_cref_general:NNNnnN \c_false_bool #1 #2 { #3 } { #4 } \crefthe_namecref_original:w
  }
\NewDocumentCommand \nameCrefthe { t- t+ O{} m }
  {
    \bool_gset_true:N \g__crefthe_uppercase_bool
    \__crefthe_cref_general:NNNnnN \c_false_bool #1 #2 { #3 } { #4 } \crefthe_nameCref_original:w
  }
\NewDocumentCommand \namecrefsthe { t- t+ O{} m }
  {
    \bool_gset_false:N \g__crefthe_uppercase_bool
    \__crefthe_cref_general:NNNnnN \c_false_bool #1 #2 { #3 } { #4 } \crefthe_namecrefs_original:w
  }
\NewDocumentCommand \nameCrefsthe { t- t+ O{} m }
  {
    \bool_gset_true:N \g__crefthe_uppercase_bool
    \__crefthe_cref_general:NNNnnN \c_false_bool #1 #2 { #3 } { #4 } \crefthe_nameCrefs_original:w
  }

\keys_define:nn { crefthe-setting }
  {
    , variant       .tl_set:N           = \l__crefthe_variant_tl
    , unknown       .code:n             = {
                                            \bool_gset_true:N \g__crefthe_has_prep_bool
                                            \tl_set:No \l__crefthe_prep_tl { \l_keys_key_tl }
                                          }
  }

\cs_new_protected:Nn \__crefthe_cref_general:NNNnnN
  % #1 = star or not
  % #2 = -
  % #3 = +
  % #4 = key-value
  % #5 = label
  % #6 = original command, such as \crefthe_cref_original:w or \crefthe_Cref_original:w
  {
    \tl_set:Nn \l__crefthe_variant_tl {}
    \bool_gset_false:N \g__crefthe_has_prep_bool
    \tl_set:Nn \l__crefthe_prep_tl {}

    \keys_set:nn { crefthe-setting } { #4 }

    \bool_if:NTF #2
      { \tl_set:No \l__crefthe_prep_once_tl { \l__crefthe_prep_tl } }
      {
        \bool_if:NTF #3
          { \tl_set:No \l__crefthe_prep_each_tl { \l__crefthe_prep_tl } }
          {
            % \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:No \l__crefthe_prep_once_tl { \l__crefthe_prep_tl } }
                { + } { \tl_set:No \l__crefthe_prep_each_tl  { \l__crefthe_prep_tl } }
              }
          }
      }
    \group_begin:
    \bool_if:NTF #1
      { #6 * { #5 } }
      { #6   { #5 } }
    \group_end:
    \tl_gclear:N \l__crefthe_prep_each_tl
  }

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

\NewDocumentCommand \crefthename { m O{} m O{} m }
  {
    \__crefthe_name_general:nnnnnn { #1 } { #2 } { #3 } { #4 } { #5 } { c }
  }

\NewDocumentCommand \Crefthename { m O{} m O{} m }
  {
    \__crefthe_name_general:nnnnnn { #1 } { #2 } { #3 } { #4 } { #5 } { C }
  }

\cs_new:Npn \crefthe_retrieve_space: { \skip_horizontal:n { -\tex_fontdimen:D 2 \tex_font:D~plus -\tex_fontdimen:D 3 \tex_font:D~minus -\tex_fontdimen:D 4 \tex_font:D } }

\cs_new:Nn \crefthe_empty_adjust:n
  {
    \tl_if_blank:nTF { #1 }
      {
        \crefthe_retrieve_space:
      }
      {
        #1
      }
  }

\cs_new_protected:Nn \__crefthe_name_general:nnnnnn
  % #1 = environment name
  % #2 = singular definite article
  % #3 = singular name
  % #4 = plural definite article
  % #5 = plural name
  % #6 = c or C
  {
    \__crefthe_name_general_do:nnnnnn
      { #1 }
      { \crefthemark { \crefthe_empty_adjust:n { #2 } } }
      { \crefthe_empty_adjust:n { #3 } }
      { \crefthemark { \crefthe_empty_adjust:n { #4 } } }
      { \crefthe_empty_adjust:n { #5 } }
      { #6 }
  }

\cs_new_protected:Nn \__crefthe_name_general_do:nnnnnn
  {
    \use:c { crefthe_#6refname_original:w } { #1 } { #2 #3 } { #4 #5 }
    \bool_if:NT \l__crefthe_nameinlink_bool
      {
        \hook_gput_code:nnn { begindocument } { crefthe }
          {
            \crefthe_patch_format:nnnn { #6ref@#1@format } { #6ref@#1@name } { #2 } { #3 }
            \crefthe_patch_format:nnnn { #6ref@#1@format@first } { #6ref@#1@name@plural } { #4 } { #5 }
            \crefthe_patch_format:nnnn { #6refrange@#1@format } { #6ref@#1@name@plural } { #4 } { #5 }
            \crefthe_patch_format:nnnn { #6refrange@#1@format@first } { #6ref@#1@name@plural } { #4 } { #5 }
          }
      }
  }

\seq_new:N \g__crefthe_already_patched_seq

\cs_new_protected:Nn \crefthe_patch_format:nnnn
  % #1 = name of the format command
  % #2 = name of the command after the hyperlink beginning mark
  % #3 = new content before the mark
  % #4 = new content after the mark
  {
    \seq_if_in:NnF \g__crefthe_already_patched_seq { #1 - #2 }
      {
        \makeatletter
        \tl_set:No \l_tmpa_tl { #3 }
        \tl_set:No \l_tmpb_tl { #4 }
        \exp_args:Nc \regexpatchcmd { #1 }
          { (\cP\# .) \c{ #2 } }
          { \u{l_tmpa_tl} \1 \u{l_tmpb_tl} }
          { } { \msg_warning:nnn { crefthe } { format-patch-failed } { #1 } }
        \makeatother
        \seq_gput_right:Nn \g__crefthe_already_patched_seq { #1 - #2 }
      }
  }

\msg_new:nnn { crefthe }
  { format-patch-failed }
  { Failed~to~patch~the~format~"\iow_char:N \\#1"! }

\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_gset:Nx \l__crefthe_prep_each_tl
      { \text_lowercase:V \l__crefthe_prep_each_tl }
    \str_if_eq:eeF { \str_tail:n { #1 } } { ' } { ~ }
    \bool_gset_false:N \g__crefthe_uppercase_bool
  }

\prg_generate_conditional_variant:Nnn \str_case_e:nn { ev } { T, F, TF }
\cs_new:Nn \crefthe_contraction:nn
  {
    \exp_args:Ne \__crefthe_contraction:nnn
      { \text_lowercase:n { #2 } } { #1 } { #2 }
  }
\cs_generate_variant:Nn \crefthe_contraction:nn { V, Ve }
\cs_new:Nn \__crefthe_contraction:nnn
  {
    % #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:evF { #2~#1 }
                  { c_crefthe_contraction_rule_ \languagename _tl }
                  { #2~#1 }
              }
          }
          { #2~#3 }
      }
  }
\cs_new:Nn \__crefthe_conditional_uppercase:n
  {
    \bool_if:NTF \g__crefthe_uppercase_bool
      { \text_titlecase_first:n }
      { \use:n }
        { #1 }
  }

\NewDocumentCommand \crefthepatchname { m }
  {
    \clist_map_inline:nn { #1 }
      {
        \crefthe_patch_name:n { ##1 }
      }
  }

\cs_new:Nn \crefthe_patch_name:n
  {
    \tl_gput_left:cn { cref@#1@name        } { \crefthemark { \crefthe_empty_adjust:n {} } }
    \tl_gput_left:cn { cref@#1@name@plural } { \crefthemark { \crefthe_empty_adjust:n {} } }
    \tl_gput_left:cn { Cref@#1@name        } { \crefthemark { \crefthe_empty_adjust:n {} } }
    \tl_gput_left:cn { Cref@#1@name@plural } { \crefthemark { \crefthe_empty_adjust:n {} } }
  }

\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_ngerman_tl
  {
    { an~dem  }     { am   }
    { an~das  }     { ans  }
    { bei~dem }     { beim }
    { in~dem  }     { im   }
    { in~das  }     { ins  }
    { von~dem }     { vom  }
    { zu~dem  }     { zum  }
    { zu~der  }     { zur  }
    { An~dem  }     { Am   }
    { An~das  }     { Ans  }
    { Bei~dem }     { Beim }
    { In~dem  }     { Im   }
    { In~das  }     { Ins  }
    { Von~dem }     { Vom  }
    { Zu~dem  }     { Zum  }
    { Zu~der  }     { Zur  }
  }
\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'.
6
  • Are the values Nominativ, Genitiv, Dativ and Akkusativ evaluated at use time or at define time? Your MWE doesn't show a usage of your macro only the definition. This way we can't really experiment with anything.
    – Skillmon
    Commented Jul 20, 2023 at 13:23
  • @Skillmon To my understanding, it should just be a change of format of the input, that is, I would like to convert the input as in the first code block (like Nominativ = [der]{Satz}[die]{Sätze}) into the format of the second code block. It is for this reason that I didn't put specific code about my package to make things complex. However, to make this conversion, some intermediate macros (like those produced by my code) might be needed, these macros need to be expanded before other commands receive them, that is, only their values are needed.
    – Jinwen
    Commented Jul 20, 2023 at 13:33
  • @Skillmon But thanks for the reminding. I shall try to add some specific usage in the MWE for testing. I'm not sure if this would be helpful but this is indeed how I check if everything works finely.
    – Jinwen
    Commented Jul 20, 2023 at 13:37
  • There is nothing that sets \l__crefthe_variant_tl it seems.
    – Skillmon
    Commented Jul 20, 2023 at 14:44
  • @Skillmon Ah, thank you for pointing this out, I just defined it the day before yesterday and haven't uploaded that version to CTAN. Perhaps I should post the updated code in the question.
    – Jinwen
    Commented Jul 20, 2023 at 15:04

1 Answer 1

2

I want to note that I don't like this code particularly much. I think the interface is unclean, and you should rather change \crefthename to support an optional argument specifying the variant.

However the following does what you want in a rather straight forward way. We need a few too many variables for my taste, but 10 is still fine, I hope. One could optimize a bit for speed dropping \tl_item:Nn and using \exp_args:No \exp_not:o { \exp_after:wN \use_i:nnnn \l__crefthe_<variable>_tl } instead, but I doubt it's worth it, the code has too many more severe performance issues for that optimisation to make a change.

Anyway, here goes nothing:

\documentclass{article}

\usepackage{amsthm}
\usepackage{crefthe}
\newtheorem{testenv}{Testenv}

\begin{document}

\ExplSyntaxOn

\NewDocumentCommand \crefthevariantname { m m }
  {
    \crefthe_parse_variant:n { #2 }
    \crefthe_pass_variant_to:n { \crefthename {#1} }
  }

% sn: singular noun, pn: plural noun, sa: singular article, pa: plural article
\tl_new:N \l__crefthe_variant_sn_tl
\tl_new:N \l__crefthe_variant_pn_tl
\tl_new:N \l__crefthe_variant_sa_tl
\tl_new:N \l__crefthe_variant_pa_tl
\tl_new:N \l__crefthe_variant_default_sn_tl
\tl_new:N \l__crefthe_variant_default_pn_tl
\tl_new:N \l__crefthe_variant_default_sa_tl
\tl_new:N \l__crefthe_variant_default_pa_tl
\bool_new:N \l__crefthe_variant_default_bool
\cs_new_protected:Npn \crefthe_parse_variant:n #1
  {
    \tl_clear:N \l__crefthe_variant_singular_noun_tl
    \tl_clear:N \l__crefthe_variant_plural_noun_tl
    \tl_clear:N \l__crefthe_variant_singular_article_tl
    \tl_clear:N \l__crefthe_variant_plural_article_tl
    \tl_clear:N \l__crefthe_variant_default_sn_tl
    \tl_clear:N \l__crefthe_variant_default_pn_tl
    \tl_clear:N \l__crefthe_variant_default_sa_tl
    \tl_clear:N \l__crefthe_variant_default_pa_tl
    \bool_set_false:N \l__crefthe_variant_default_bool
    \keyval_parse:NNn
      \__crefthe_parse_variant_default:n
      \__crefthe_parse_variant_value:nn
      {#1}
  }
\tl_new:N \l__crefthe_parsed_tl
% this is wrong! That's not a document command, interface should be cleaner.
\NewDocumentCommand \__crefthe_variant_parser:w { O{} m O{} m }
  {
    \tl_set:Nn \l__crefthe_parsed_tl { {#1} {#2} {#3} {#4} }
    \use_none_delimit_by_q_stop:w
  }
\cs_new_protected:Npn \__crefthe_parse_variant_default:n #1
  {
    \bool_set_true:N \l__crefthe_variant_default_bool
    \__crefthe_variant_parser:w #1 {} {} \q_stop
    \tl_set:Nx \l__crefthe_variant_default_sa_tl
      { \tl_item:Nn \l__crefthe_parsed_tl {1} }
    \tl_set:Nx \l__crefthe_variant_default_sn_tl
      { \tl_item:Nn \l__crefthe_parsed_tl {2} }
    \tl_set:Nx \l__crefthe_variant_default_pa_tl
      { \tl_item:Nn \l__crefthe_parsed_tl {3} }
    \tl_set:Nx \l__crefthe_variant_default_pn_tl
      { \tl_item:Nn \l__crefthe_parsed_tl {4} }
  }
\cs_new_protected:Npn \__crefthe_parse_variant_value:nn #1#2
  {
    \__crefthe_variant_parser:w #2 {} {} \q_stop
    \tl_put_right:Nx \l__crefthe_variant_sa_tl
      { { \tl_to_str:n {#1} } { \tl_item:Nn \l__crefthe_parsed_tl {1} } }
    \tl_put_right:Nx \l__crefthe_variant_sn_tl
      { { \tl_to_str:n {#1} } { \tl_item:Nn \l__crefthe_parsed_tl {2} } }
    \tl_put_right:Nx \l__crefthe_variant_pa_tl
      { { \tl_to_str:n {#1} } { \tl_item:Nn \l__crefthe_parsed_tl {3} } }
    \tl_put_right:Nx \l__crefthe_variant_pn_tl
      { { \tl_to_str:n {#1} } { \tl_item:Nn \l__crefthe_parsed_tl {4} } }
  }

\cs_new_protected:Npn \crefthe_pass_variant_to:n #1
  {
    \use:e
      {
        \exp_not:n {#1}
        \bool_if:NTF \l__crefthe_variant_default_bool
          {
            [
              \exp_not:n { \str_case:NnF \l__crefthe_variant_tl }
                { \exp_not:o \l__crefthe_variant_sa_tl }
                { \exp_not:o \l__crefthe_variant_default_sa_tl }
            ]
            {
              \exp_not:n { \str_case:NnF \l__crefthe_variant_tl }
                { \exp_not:o \l__crefthe_variant_sn_tl }
                { \exp_not:o \l__crefthe_variant_default_sn_tl }
            }
            [
              \exp_not:n { \str_case:NnF \l__crefthe_variant_tl }
                { \exp_not:o \l__crefthe_variant_pa_tl }
                { \exp_not:o \l__crefthe_variant_default_pa_tl }
            ]
            {
              \exp_not:n { \str_case:NnF \l__crefthe_variant_tl }
                { \exp_not:o \l__crefthe_variant_pa_tl }
                { \exp_not:o \l__crefthe_variant_default_pa_tl }
            }
          }
          {
            [
              \exp_not:n { \str_case:Nn \l__crefthe_variant_tl }
                { \exp_not:o \l__crefthe_variant_sa_tl }
            ]
            {
              \exp_not:n { \str_case:Nn \l__crefthe_variant_tl }
                { \exp_not:o \l__crefthe_variant_sn_tl }
            }
            [
              \exp_not:n { \str_case:Nn \l__crefthe_variant_tl }
                { \exp_not:o \l__crefthe_variant_pa_tl }
            ]
            {
              \exp_not:n { \str_case:Nn \l__crefthe_variant_tl }
                { \exp_not:o \l__crefthe_variant_pa_tl }
            }
          }
      }
  }

\ExplSyntaxOff

\crefthevariantname{testenv}
  {
    [0]{Zero}[00]{Zero}
    , Nominativ = [1]{One}[11]{Ones}
    , Genitiv   = [2]{Two}[22]{Twos}
    , Dativ     = [3]{Three}[33]{Threes}
    , Akkusativ = [4]{Four}[44]{Fours}
  }

\begin{testenv}\label{testenv:test}
    Environment for testing...
\end{testenv}

\crefthe{testenv:test}

\crefthe[variant=Nominativ]{testenv:test}

\crefthe[variant=Genitiv]{testenv:test}

\crefthe[variant=Dativ]{testenv:test}

\crefthe[variant=Akkusativ]{testenv:test}

\end{document}

enter image description here

6
  • Thank you for this, I shall take a close look at it. Regarding you opinion about the code of the package, actually I feel the same way. crefthe is the first package I wrote after learning the very basics of expl3, and at first it is really just a small extension to cleveref: the syntax for, say, \crefthename, is designed so that when no optional arguments are given would fall back to \crefname in cleveref. New package like create-theorem was then designed based on it, making it harder to change. Also, the code is not extendable, so one cannot easily add new language support.
    – Jinwen
    Commented Jul 20, 2023 at 22:32
  • And it is after some time that I discovered that German grammar is more sophisticated than I expected and the original design did not fit German -- by the time the code has already shaped. However, though currently one cannot have the same interface defining names within crefthe, I can make the interface in create-theorem to be consistent. In addition, in order to solve the problem of specifying the feminine, masculine, and neuter of the word, I've been considering a new interface similar to that of zref-clever. Anyway, I hope to hear your opinion on further improvements.
    – Jinwen
    Commented Jul 20, 2023 at 22:42
  • Would you mind if I include you in the acknowledgement section? Without your help I couldn't have established German support so quickly. If you don't mind, shall I write your name as Jonathan P. Spratte as put in your CTAN page?
    – Jinwen
    Commented Jul 20, 2023 at 23:45
  • @Jinwen fine by me, you might acknowledge me for this small code suggestion :)
    – Skillmon
    Commented Jul 21, 2023 at 6:04
  • May I ask what is the purpose of {} {} before \q_stop? Removing them doesn't seem to cause any trouble.
    – Jinwen
    Commented Jul 21, 2023 at 14:07

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .