
Me gustaría construir una macro que pueda usarse para definir comandos. Esos comandos se comportan como variables o estructuras de variables, de modo que pueden contener múltiples valores. El "miembro" se pasa mediante un argumento opcional. Lo uso para definir entornos de plantilla y tener diferentes cadenas para diferentes idiomas, que deben estar presentes en el documento simultáneamente.
Esto es lo que tenía y 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}}%
}
Primero se establecen los valores predeterminados. Luego se crea un comando que establece los valores según el argumento opcional. Si no hay ninguno, establezca el valor para todas las configuraciones regionales.
% 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}
Intenté modificar el comando para aceptar configuraciones regionales arbitrarias, pero algo no funciona.
\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}}%
}
Si establezco valores que se utilizan, todo está bien y excelente. Cuando no se establecen valores, mis entornos personalizados se interrumpen y el mensaje predeterminado que se muestra es simplemente Use \Cmd[] to...
, es decir, sin los nombres de configuración regional.
¿Alguna idea de lo que está pasando?
Respuesta1
Como se menciona en el comentario, su principal problema fue que \n
en el texto de reemplazo \@namedef
no se amplió su valor. Así, el texto de sustitución, Faculty:fi
por ejemplo, quedó literalmente
{\scriptsize (Use {\tt\textbackslash #2[\n]} to replace this text.)}%
con \n
. En la mayoría de los contextos donde se llamaría a la macro \n
no estaría definida y obtendría un error. Quiere que el texto de reemplazo de la macro se construya de modo que \n
se convierta en su expansión, es decir, fi
o en
. La forma más sencilla de hacerlo correctamente es probablemente una macro auxiliar. Esta respuesta mostrará dos enfoques: uno con etoolbox
sus macros de lista y otro con su código que utiliza \expandafter
una macro auxiliar. etoolbox
Los bucles de funcionan pasando el valor de la variable del bucle directamente como argumento a una macro auxiliar.
En el primer argumento de \@namedef
(es decir, el #2:\n
), \n
se expandiría automáticamente para que realmente estuviera fi
allí.
Aquí hay una solución que utiliza etoolbox
y su lista de macros. Algunas explicaciones están en línea. La ventaja de este enfoque es que no hay necesidad de expandir la variable del bucle ( \n
), ya que el bucle se implementa directamente como una macro (normalmente requiere una 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}
Si desea mantener su versión con \foreach
y \ifthenelse
, puede utilizar una función auxiliar.
El truco es que necesitamos expandirnos \n
para obtener su valor real. Esto es posible con \expandafter
, pero para evitar tener que usarlo con precisión quirúrgica para saltar muchas fichas, es útil una función auxiliar. Dado que es un poco más fácil expandir el primer argumento de una macro con \expandafter
s en lugar de un argumento posterior, la función auxiliar utiliza el orden de los argumentos ligeramente inesperado {<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}
Por cierto: utilicé \ttfamily
en lugar de \tt
, porque los comandos de fuentes de dos letras están obsoletos en LaTeX2e (https://texfaq.org/FAQ-2letterfontcmd,¿Los comandos de estilo de fuente de dos letras (\bf, \it,…) alguna vez resucitarán en LaTeX?).