Обобщение макроса второго порядка с циклами

Обобщение макроса второго порядка с циклами

Я хотел бы создать макрос, который можно использовать для определения команд. Эти команды ведут себя как переменные или структуры переменных, так что они могут содержать несколько значений. «Член» передается через необязательный аргумент. Я использую его для определения шаблонных сред и для того, чтобы иметь разные строки для разных языков, которые должны присутствовать в документе одновременно.

Вот что у меня было и работает.

\newcommand\MakeLocaleVar[1]{%
    \global\@namedef{#1:en}{{\scriptsize (Use {\tt\textbackslash #1[en]} to replace this text.)}}%
    \global\@namedef{#1:fi}{{\scriptsize (Use {\tt\textbackslash #1[fi]} to replace this text.)}}%
    \expandafter\newcommand\csname #1\endcsname[2][]{%
        \ifthenelse{\equal{##1}{}}{%
            \global\@namedef{#1:en}{##2}%
            \global\@namedef{#1:fi}{##2}%
        }{%
            \global\@namedef{#1:##1}{##2}%
        }%
    }%
    \expandafter\newcommand\csname Emit#1\endcsname[1][en]{\@nameuse{#1:##1}}%
}

Сначала устанавливаются значения по умолчанию. Затем создается команда, которая устанавливает значения в соответствии с необязательным аргументом. Если нет, установить значение для всех локалей.

% In cls: define command
\MakeLocaleVar{Faculty}

% In main tex: set values
\Faculty{This faculty} % for all values
\Faculty[fi]{Tämä tiedekunta} % for a specific one

% In cls environments: use values
\EmitFaculty[en]
\EmitFaculty[fi]

% Now in addition I'd like to be able to:
\MakeLocaleVar[en,fi,de]{Faculty}

Я пытался изменить команду, чтобы она принимала произвольные локали, но что-то не работает.

\newcommand\MakeLocaleVar[2][en,fi]{%
    \foreach \n in {#1}{%
        \global\@namedef{#2:\n}{%
            {\scriptsize (Use {\tt\textbackslash #2[\n]} to replace this text.)}%
        }%
    }%
    \expandafter\newcommand\csname #2\endcsname[2][]{%
        \ifthenelse{\equal{##1}{}}{%
            \foreach \n in {#1}{%
                \global\@namedef{#2:\n}{##2}%
            }%
        }{%
            \global\@namedef{#2:##1}{##2}%
        }%
    }%
    \expandafter\newcommand\csname Emit#2\endcsname[1][en]{\@nameuse{#2:##1}}%
}

Если я устанавливаю используемые значения, все отлично и замечательно. Когда значения не установлены, мои пользовательские среды ломаются, и сообщение по умолчанию просто Use \Cmd[] to..., без названий локалей.

Есть идеи, что происходит?

решение1

Как упоминалось в комментарии, ваша главная проблема заключалась в том, что \nв тексте замены \@namedefне было расширено до его значения. Текст замены для, Faculty:fiнапример, таким образом оставался буквально

{\scriptsize (Use {\tt\textbackslash #2[\n]} to replace this text.)}%

с \n. В большинстве контекстов, где макрос будет вызван, \nбудет неопределенным, и вы получите ошибку. Вы хотите, чтобы текст замены макроса был сконструирован так, чтобы он \nстал его расширением, т. е. fiили en. Самый простой способ сделать это правильно — это, вероятно, вспомогательный макрос. В этом ответе будут показаны два подхода: один с etoolboxи его макросами списка, а другой с вашим кодом, который использует \expandafterи вспомогательный макрос. etoolboxЦиклы работают, передавая значение переменной цикла непосредственно в качестве аргумента вспомогательному макросу.

В первом аргументе \@namedef(т.е. #2:\n), , \nбудет автоматически расширен, так что он действительно окажется fiтам.


Вот решение с использованием etoolboxи его макросов списка. Некоторые пояснения встроены. Преимущество этого подхода в том, что нет необходимости в расширении переменной цикла ( \n), поскольку цикл напрямую реализован как макрос (обычно требующий вспомогательного макроса).

\documentclass[english,finnish]{article}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage{babel}

\usepackage{etoolbox}

\makeatletter
% {<name>}{<lang>}
\newcommand*{\mlv@defaultvalue}[2]{%
  \csdef{#1@#2}{%
    {\scriptsize (Use {\ttfamily\textbackslash #1[#2]} to replace this text.)}}}
% {<repl. text>}{<name>}{<lang>}
% the unusual order instead of the more natural {<name>}{<lang>}{<repl. text>}
% makes for a more elegant call to \forcsvlist
\newcommand*{\mlv@realvalue}[3]{%
  \csdef{#2@#3}{#1}}

% [<langs>]{<name>}
% \forcsvlist{<macro>}{<item_1, item_2, ..., item_n>}
% calls <macro> once with each item_i as last argument
% <macro>{<item_1>}, <macro>{<item_2>}
% <macro> can already bring a few arguments of its own
% it could be <marco>{<fixed_1>}...{<fixed_n>} and then the invocations
% would become <marco>{<fixed_1>}...{<fixed_n>}{<item_1>} etc.
% Since the items are the <lang> argument it must be the last
% argument to \mlv@defaultvalue and \mlv@realvalue
\newcommand\MakeLocaleVar[2][en,fi]{%
  \forcsvlist{\mlv@defaultvalue{#2}}{#1}%
  \expandafter\newcommand\csname #2\endcsname[2][]{%
    \ifblank{##1}
      {\forcsvlist{\mlv@realvalue{##2}{#2}}{#1}}%
      {\mlv@realvalue{##2}{#2}{##1}}}%
  \expandafter\newcommand\csname Emit#2\endcsname[1][en]{\csuse{#2@##1}}%
}
\makeatother

\begin{document}

\MakeLocaleVar{Faculty}

% In main tex: set values
\Faculty{This faculty} % for all values
\Faculty[fi]{Tämä tiedekunta} % for a specific one

% In cls environments: use values
\EmitFaculty[en]
\EmitFaculty[fi]

% Now in addition I'd like to be able to:
\MakeLocaleVar[en,fi,de]{Gaculty}

\EmitGaculty[en]
\EmitGaculty[fi]
\EmitGaculty[de]

\Gaculty{Foo}
\EmitGaculty[en]
\EmitGaculty[fi]
\EmitGaculty[de]

\Gaculty[fi]{Föö}
\EmitGaculty[en]
\EmitGaculty[fi]
\EmitGaculty[de]
\end{document}

Этот факультет Tämä tiedekunta//(Используйте \Gaculty[en] для замены этого текста.) (Используйте \Gaculty[fi] для замены этого текста.) (Используйте \Gaculty[de] для замены этого текста.)//Foo Foo Foo//Foo Föö Foo


Если вы хотите придерживаться своей версии с \foreachи \ifthenelse, вы можете воспользоваться вспомогательной функцией.

Хитрость в том, что нам нужно расширить \nтак, чтобы получить его фактическое значение. Это возможно с помощью \expandafter, но чтобы избежать необходимости использовать его с хирургической точностью для перепрыгивания через множество токенов, полезна вспомогательная функция. Поскольку немного проще расширить первый аргумент макроса с помощью \expandafters вместо последующего аргумента, вспомогательная функция использует немного неожиданный порядок аргументов {<lang>}{<name>}.

\documentclass[english,finnish]{article}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage{babel}

\usepackage{ifthen}
\usepackage{tikz}

\makeatletter
% helper for easier control of expansion
% {<lang>}{<name>}
\newcommand*{\mlv@helper}[2]{%
  \global\@namedef{#2:#1}{%
    {\scriptsize (Use {\ttfamily\textbackslash #2[#1]} to replace this text.)}%
  }%
}

% \n must be expanded to be useful.
% The first argument of \@namedef automatically expands it,
% but the second does not.
% Here we use a helper function to expand \n
% before it is processed.
\newcommand\MakeLocaleVar[2][en,fi]{%
    \foreach \n in {#1}{%
        \expandafter\mlv@helper\expandafter{\n}{#2}%
    }%
    \expandafter\newcommand\csname #2\endcsname[2][]{%
        \ifthenelse{\equal{##1}{}}{%
            \foreach \n in {#1}{%
                \global\@namedef{#2:\n}{##2}%
            }%
        }{%
            \global\@namedef{#2:##1}{##2}%
        }%
    }%
    \expandafter\newcommand\csname Emit#2\endcsname[1][en]{\@nameuse{#2:##1}}%
}
\makeatother

\begin{document}

\MakeLocaleVar{Faculty}

% In main tex: set values
\Faculty{This faculty} % for all values
\Faculty[fi]{Tämä tiedekunta} % for a specific one

% In cls environments: use values
\EmitFaculty[en]
\EmitFaculty[fi]

% Now in addition I'd like to be able to:
\MakeLocaleVar[en,fi,de]{Gaculty}

\EmitGaculty[en]
\EmitGaculty[fi]
\EmitGaculty[de]

\Gaculty{Foo}
\EmitGaculty[en]
\EmitGaculty[fi]
\EmitGaculty[de]

\Gaculty[fi]{Föö}
\EmitGaculty[en]
\EmitGaculty[fi]
\EmitGaculty[de]
\end{document}

Кстати: я использовал \ttfamilyвместо \tt, потому что двухбуквенные команды шрифтов устарели в LaTeX2e (https://texfaq.org/FAQ-2letterfontcmd,Будут ли когда-нибудь возрождены в LaTeX двухбуквенные команды стилей шрифта (\bf , \it , …)?).

Связанный контент