Expansión del argumento macro en el medio ambiente

Expansión del argumento macro en el medio ambiente

Me gustaría generar un entorno que cambie un poco según un argumento dado para ello. El motivo es que el documento tiene diferentes configuraciones regionales. He definido un comando EmitLanguageStrque determina el argumento de otherlanguageacuerdo con el argumento que se pasó. Por ejemplo, \EmitLanguageStr[en] -> english. Consulte estas preguntas para obtener más contexto: [1,2]. Aquí está el entorno simplificado.

\renewenvironment{abstract}[1][en]{%
    \section*{\EmitAbstractStr[#1]}
    \begin{otherlanguage}{\EmitLanguageStr[#1]}
        \@author\par
        \textbf{\EmitTitle[#1]}\par
}{%
    \end{otherlanguage}
}

Resulta que otras personas han tenido el mismo problema (q), pero la solución no me funcionó del todo. La respuesta sugiere que envolver el entorno de una de dos maneras permitiría ampliar el argumento:

% Create a macro
\newcommand\testlang{english}

% Expand the argument once:
\newenvironment{Otherlanguage}[1]{%
    \expandafter\otherlanguage\expandafter{#1}%
}{\endotherlanguage}

% Alternative: argument is fully expanded
\newenvironment{Otherlanguage}[1]{%
    \begingroup
    \edef\temp{\endgroup\noexpand\otherlanguage{#1}}%
    \temp
}{\endotherlanguage}

% usage    
\begin{Otherlanguage}{\testlang}
    ...
\end{Otherlangauge}

Primero, sin estos envoltorios, babel muestra un mensaje de error que indica que el idioma EmitLanguageStr[en]no está definido. Eso se abordó en la pregunta vinculada. Sin embargo, las soluciones presentadas producen otro tipo de error: Use of \EmitLanguageStr doesn't match its definition.

¿Cómo se puede ampliar adecuadamente el comando?


Aquí hay un ejemplo completo de la situación. Esto funciona, pero al cambiar el argumento de Otherlanguageal del comentario se genera el error descrito.

\documentclass[a4paper,12pt]{article}

\usepackage{ifthen}
\usepackage{pgffor}

\makeatletter

\newcommand\SetDefaultValue[2]{%
    \global\@namedef{#2:#1}{%
        {\scriptsize (Use {\tt\textbackslash #2[#1]} to replace this text.)}%
    }%
}

\newcommand\MakeLocaleVar[2][en,fi]{%
    \foreach \n in {#1}{%
        \expandafter\SetDefaultValue\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}}%
}

% Variables set in document
\MakeLocaleVar{Title}
\MakeLocaleVar{Year}

%\newenvironment{Otherlanguage}[1]{%
%   \expandafter\otherlanguage\expandafter{#1}%
%}{\endotherlanguage}

\newenvironment{Otherlanguage}[1]{%
    \begingroup
    \edef\temp{\endgroup\noexpand\otherlanguage{#1}}%
    \temp
}{\endotherlanguage}

\MakeLocaleVar{AbstractStr}
\AbstractStr[en]{ABSTRACT}
\AbstractStr[fi]{TIIVISTELMÄ}

\MakeLocaleVar{LanguageStr}
\LanguageStr[en]{english}
\LanguageStr[fi]{finnish}

\renewenvironment{abstract}[1][en]{%
    \section*{\EmitAbstractStr[#1]}
    \begin{Otherlanguage}{english}%\EmitLanguageStr[#1]
        \@author\par
        \textbf{\EmitTitle[#1]}\par
        \EmitYear\par
        \par
    }{%
    \end{Otherlanguage}
}

\makeatother

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

\author{Handsome Devil}
\Year{2019}

\Title[en]{Title in English}
\Title[fi]{Otsikko suomeksi}

\begin{document}
\selectlanguage{english}

\begin{abstract}
    Abstract environment in English.
\end{abstract}

\begin{abstract}[fi]
    Abstract environment in Finnish.
\end{abstract}
\end{document}

Respuesta1

\MakeLocaleVar{LanguageStr}causas \EmitLanguageStrque se definen como:

\newcommand\EmitLanguageStr[1][en]{\@nameuse{LanguageStr:#1}}%

\EmitLanguageStrprocesa un argumento opcional.

Si miras a \EmitLanguageStrtravés de \show\EmitLanguageStr, encontrarás:

> \EmitLanguageStr=macro:
->\@protected@testopt \EmitLanguageStr \\EmitLanguageStr {en}.

Mirando \@protected@testoptlos rendimientos:

> \@protected@testopt=macro:
#1->\ifx \protect \@typeset@protect \expandafter \@testopt \else \@x@protect #1
\fi .

Mirando \@testoptlos rendimientos:

> \@testopt=\long macro:
#1#2->\kernel@ifnextchar [{#1}{#1[{#2}]}.

Mirando \kernel@ifnextcharlos rendimientos:

> \kernel@ifnextchar=\long macro:
#1#2#3->\let \reserved@d =#1\def \reserved@a {#2}\def \reserved@b {#3}\futurele
t \@let@token \@ifnch .

Mirando \@ifnchlos rendimientos:

> \@ifnch=macro:
->\ifx \@let@token \@sptoken \let \reserved@c \@xifnch \else \ifx \@let@token \
reserved@d \let \reserved@c \reserved@a \else \let \reserved@c \reserved@b \fi 
\fi \reserved@c .

Mirando \@xifnchlos rendimientos:

> \@xifnch=macro:
 ->\futurelet \@let@token \@ifnch .

Como puede ver, hay que jugar mucho con asignaciones \lety no expandibles \futureletpara verificar la presencia del argumento opcional a través de \kernel@ifnextchar [.

Por lo tanto \EmitLanguageStrno sólo entrega aquellos tokens de caracteres que forman el nombre del idioma. También produce una gran cantidad de tokens de asignación no expandibles como \lety \futureletpara verificar la presencia del argumento opcional.
Estos tokens no desaparecerán dentro de un contexto de expansión pura, mientras que obtener el nombre del idioma para pasarlo como argumento a un entorno sería un contexto de expansión pura. Después de la expansión, estos tokens de asignación no expandibles permanecen en el flujo de tokens porque mientras la expansión tiene lugar en la "garganta" de (La)TeX, la realización de las asignaciones tiene lugar en el "estómago" de (La)TeX.

En resumen:

Siempre que \EmitLanguageStrprocese un argumento opcional mediante el cual las técnicas para detectar la presencia de un argumento opcional impliquen tokens de asignación no expandibles que no desaparecen durante la expansión, no se puede utilizar \EmitLanguageStren contextos de expansión pura para obtener solo esos tokens. que forman el nombre de la lengua.

Pero puede, por ejemplo, crear una variante de \EmitLanguageStrcon un argumento adicional que contenga tokens a los que se pasará la cadena emitida como argumento. Yo llamo a esa macro \PassLanguageStrTo:

\documentclass[a4paper,12pt]{article}

\usepackage{ifthen}
\usepackage{pgffor}

\makeatletter

\newcommand\SetDefaultValue[2]{%
    \global\@namedef{#2:#1}{%
        {\scriptsize (Use {\tt\textbackslash #2[#1]} to replace this text.)}%
    }%
}

\newcommand\PassFirstToSecond[2]{#2{#1}}%

\newcommand\MakeLocaleVar[2][en,fi]{%
    \foreach \n in {#1}{%
        \expandafter\SetDefaultValue\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}}%
    \expandafter\newcommand\csname Pass#2To\endcsname[2][en]{%
      \expandafter\expandafter\expandafter\PassFirstToSecond
      \expandafter\expandafter\expandafter{\csname#2:##1\endcsname}{##2}%
    }%
}

% Variables set in document
\MakeLocaleVar{Title}
\MakeLocaleVar{Year}


\MakeLocaleVar{AbstractStr}
\AbstractStr[en]{ABSTRACT}
\AbstractStr[fi]{TIIVISTELMÄ}

\MakeLocaleVar{LanguageStr}
\LanguageStr[en]{english}
\LanguageStr[fi]{finnish}

\renewenvironment{abstract}[1][en]{%
    \section*{\EmitAbstractStr[{#1}]}%%%%
    \PassLanguageStrTo[{#1}]{\begin{otherlanguage}}%
        \@author\par
        \textbf{\EmitTitle[{#1}]}\par
        \EmitYear\par
        \par
    }{%
    \end{otherlanguage}%%%%
}

\makeatother

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

\author{Handsome Devil}
\Year{2019}

\Title[en]{Title in English}
\Title[fi]{Otsikko suomeksi}

\begin{document}
\selectlanguage{english}

\begin{abstract}
    Abstract environment in English.
\end{abstract}

\begin{abstract}[fi]
    Abstract environment in Finnish.
\end{abstract}
\end{document}

información relacionada