環境引数におけるマクロの拡張

環境引数におけるマクロの拡張

与えられた引数に応じて少し変化する環境を生成したいです。理由は、ドキュメントに異なるロケールがあるからです。渡された引数に応じてEmitLanguageStr引数を決定するコマンドを定義しましたotherlanguage。たとえば、\EmitLanguageStr[en] -> englishコマンドを定義しました。たとえば、です。詳細については、次の質問を参照してください。[12]。ここでは環境を簡素化します。

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

他の人も同じ問題を抱えていたことが判明しました(質問) ですが、この解決策は私にとってはうまくいきませんでした。答えは、環境を 2 つの方法のいずれかでラップすると、引数を拡張できると示唆しています。

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

まず、これらのラッパーがないと、babel は言語EmitLanguageStr[en]が定義されていないというエラー メッセージを表示します。これはリンクされた質問で対処されています。ただし、提示された解決策では別の種類のエラーが発生しますUse of \EmitLanguageStr doesn't match its definition

コマンドを適切に拡張するにはどうすればよいでしょうか?


状況の完全な例を以下に示します。 これは機能しますが、の引数をOtherlanguageコメント内の引数に切り替えると、説明されているエラーがスローされます。

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

答え1

\MakeLocaleVar{LanguageStr}原因は\EmitLanguageStr次のように定義されます。

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

\EmitLanguageStrオプションの引数を処理します。

\EmitLanguageStr経由で見ると\show\EmitLanguageStr、次のことがわかります。

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

\@protected@testopt利回りを見ると:

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

\@testopt利回りを見ると:

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

\kernel@ifnextchar利回りを見ると:

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

\@ifnch利回りを見ると:

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

\@xifnch利回りを見ると:

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

ご覧のとおり、 を介したオプション引数の存在のチェックには、展開不可能な\let- および -割り当てをいろいろと試すことが関係しています。\futurelet\kernel@ifnextchar [

したがって、\EmitLanguageStr言語名を形成する文字トークンだけを生成するのではありません。また、オプション引数の存在を確認するための や などの、展開不可能な割り当てトークンも多数生成します。\letこれら のトークンは、純粋な展開コンテキスト内では消えませんが、環境に引数として渡すために言語名を取得することは、純粋な展開コンテキストになります。展開後、これらの展開不可能な割り当てトークンはトークン ストリーム内に残ります。これは、展開が (La)TeX の「食道」で行われるのに対し、割り当ての実行は (La)TeX の「胃」で行われるためです。\futurelet

つまり、要約すると:

\EmitLanguageStrがオプション引数を処理し、オプション引数の存在を検出する手法自体に、展開中に消えない非展開割り当てトークンが含まれる限り、\EmitLanguageStr言語の名前を形成するトークンのみを取得するために純粋な展開コンテキストで使用することはできません。

しかし、例えば、\EmitLanguageStr出力された文字列が引数として渡されるトークンを保持する追加の引数を持つ のバリアントを作成することができます。私はこのマクロを と呼びます\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}

関連情報