Generalizando una macro de segundo orden con bucles

Generalizando una macro de segundo orden con bucles

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 \nen el texto de reemplazo \@namedefno se amplió su valor. Así, el texto de sustitución, Faculty:fipor 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 \nno estaría definida y obtendría un error. Quiere que el texto de reemplazo de la macro se construya de modo que \nse convierta en su expansión, es decir, fio en. La forma más sencilla de hacerlo correctamente es probablemente una macro auxiliar. Esta respuesta mostrará dos enfoques: uno con etoolboxsus macros de lista y otro con su código que utiliza \expandafteruna macro auxiliar. etoolboxLos 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), \nse expandiría automáticamente para que realmente estuviera fiallí.


Aquí hay una solución que utiliza etoolboxy 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}

Esta facultad Tämä tieekunta//(Usa \Gaculty[en] para reemplazar este texto.) (Usa \Gaculty[fi] para reemplazar este texto.) (Usa \Gaculty[de] para reemplazar este texto.)//Foo Foo Foo //Foo Föö Foo


Si desea mantener su versión con \foreachy \ifthenelse, puede utilizar una función auxiliar.

El truco es que necesitamos expandirnos \npara 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 \expandafters 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é \ttfamilyen 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?).

información relacionada