
Gostaria de construir uma macro que possa ser usada para definir comandos. Esses comandos se comportam como variáveis ou estruturas de variáveis, de modo que podem conter vários valores. O "membro" é passado através de um argumento opcional. Eu o utilizo para definir ambientes de templates e para ter strings diferentes para linguagens diferentes, que precisam estar presentes no documento simultaneamente.
Aqui está o que eu tinha e funciona.
\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}}%
}
Primeiro, os valores padrão são definidos. Em seguida, é criado um comando que define os valores de acordo com o argumento opcional. Se nenhum, defina o valor para todas as localidades.
% 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}
Tentei modificar o comando para aceitar localidades arbitrárias, mas algo não está funcionando.
\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}}%
}
Se eu definir valores que serão usados, tudo ficará bem e elegante. É quando os valores não são definidos que meus ambientes personalizados quebram e a mensagem padrão mostrada é apenas Use \Cmd[] to...
, portanto, sem os nomes de localidade.
Alguma ideia do que está acontecendo?
Responder1
Conforme mencionado no comentário, seu principal problema foi que o \n
no texto de substituição do \@namedef
não foi ampliado para seu valor. O texto de substituição, Faculty:fi
por exemplo, permaneceu assim literalmente
{\scriptsize (Use {\tt\textbackslash #2[\n]} to replace this text.)}%
com \n
. Na maioria dos contextos onde a macro seria chamada \n
seria indefinida e você receberia um erro. Você deseja que o texto de substituição da macro seja construído de forma que \n
se torne sua expansão, ou seja, fi
ou en
. A maneira mais fácil de fazer isso corretamente é provavelmente uma macro auxiliar. Esta resposta mostrará duas abordagens: uma com etoolbox
suas macros de lista e outra com seu código que usa \expandafter
uma macro auxiliar. etoolbox
Os loops de funcionam passando o valor da variável do loop diretamente como um argumento para uma macro auxiliar.
No primeiro argumento de \@namedef
(ou seja, o #2:\n
), o \n
seria expandido automaticamente para que realmente se tornasse fi
lá.
Aqui está uma solução usando etoolbox
e suas macros de lista. Algumas explicações estão embutidas. A vantagem desta abordagem é que não há necessidade de expansão da variável de loop ( \n
), pois o loop é implementado diretamente como uma macro (geralmente exigindo uma macro auxiliar).
\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}
Se quiser manter sua versão com \foreach
e \ifthenelse
, você pode usar uma função auxiliar.
O truque é que precisamos expandir \n
para obtermos seu valor real. Isso é possível com \expandafter
, mas para evitar ter que usá-lo com precisão cirúrgica para pular muitos tokens, uma função auxiliar é útil. Como é um pouco mais fácil expandir o primeiro argumento de uma macro com \expandafter
s em vez de um argumento posterior, a função auxiliar usa o argumento ligeiramente inesperado order {<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}
BTW: usei \ttfamily
em vez de \tt
, porque os comandos de fonte de duas letras estão obsoletos no LaTeX2e (https://texfaq.org/FAQ-2letterfontcmd,Os comandos de estilo de fonte de duas letras (\bf , \it ,…) serão ressuscitados no LaTeX?).