0

I'd like to generate an environment that changes a bit according to an argument given for it. The reason is having different locales in the document. I have defined a command, EmitLanguageStr that determines the argument to otherlanguage according to the argument that was passed. For example, \EmitLanguageStr[en] -> english. See these questions for further context: [ 1, 2 ]. Here's the environment stripped down.

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

Turns out other people have had the same problem (Q), but the solution did not quite work for me. The answer suggests that wrapping the environment in one of two ways would allow the argument to be expanded:

% 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}

First, without these wrappers, babel gives an error message that the language EmitLanguageStr[en] is not defined. That was addressed in the linked question. However, the solutions presented produce another kind of error: Use of \EmitLanguageStr doesn't match its definition.

How can the command be properly expanded?


Here's a complete example of the situation. This works, but by switching the argument of Otherlanguage to the one in the comment the described error is thrown.

\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}
  • 2
    As always on this site please provide a full minimal example instead of sniplets. That makes it a lot easier for others to test your code and provide suggestions – daleif Mar 4 at 12:28
  • Seems \EmitLanguageStr processes an optional argument. LaTeX-macros that process optional arguments usually are not fully expandable. This means \EmitLanguageStr won't expand to tokens only that form the name of a language but it also delivers assignment-tokens like \@ifnextchar/\futurelet which are used for "looking ahead" at the next token for finding out whether that is a left-bracket. Such assignment-tokens won't vanish during expansion. Besides this for debugging a compilable example is needed which lets you reproduce the erroneous/problematic behavior exactly. – Ulrich Diez Mar 4 at 12:39
  • @UlrichDiez Oh, that's good to know. Minimal example added. – Felix Mar 4 at 13:00
0

\MakeLocaleVar{LanguageStr} causes \EmitLanguageStr to be defined as:

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

\EmitLanguageStr processes an optional argument.

If you look at \EmitLanguageStr via \show\EmitLanguageStr, you'll find:

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

Looking at \@protected@testopt yields:

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

Looking at \@testopt yields:

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

Looking at \kernel@ifnextchar yields:

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

Looking at \@ifnch yields:

> \@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 .

Looking at \@xifnch yields:

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

As you can see a lot of playing around with non-expandable \let- and \futurelet-assignments is involved into the checking for the presence of the optional argument via \kernel@ifnextchar [.

Therefore \EmitLanguageStr does not only deliver those character tokens that form the name of the language. It also yields a lot of unexpandable assignment-tokens like \let and \futurelet for checking for the presence of the optional argument.
These tokens won't vanish within a pure-expansion-context while obtaining the name of the language for passing it as argument to an environment would be a pure expansion context. After expansion these unexpandable assignment-tokens remain in the token-stream because while expansion takes place in (La)TeX's "gullet", the carrying out of assignments takes place in (La)TeX's "stomach".

So in short:

As long as \EmitLanguageStr processes an optional argument whereby the techniques for detecting the presence of an optional argument themselves involve non-expandable assignment-tokens which don't vanish during expansion, you can't use \EmitLanguageStr in pure-expansion-contexts for obtaining only those tokens that form the name of the language.

But you can, e.g., create a variant of \EmitLanguageStr with an additional argument holding tokens to which the emitted string is to be passed as argument. I call that 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}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.