
Ich möchte ein Makro konstruieren, mit dem man Befehle definieren kann. Diese Befehle verhalten sich wie Variablen oder Variablenstrukturen, sodass sie mehrere Werte enthalten können. Das „Mitglied“ wird über ein optionales Argument übergeben. Ich verwende es, um Vorlagenumgebungen zu definieren und unterschiedliche Zeichenfolgen für unterschiedliche Sprachen zu haben, die gleichzeitig im Dokument vorhanden sein müssen.
Hier ist, was ich hatte und was funktioniert.
\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}}%
}
Zuerst werden die Standardwerte festgelegt. Dann wird ein Befehl erstellt, der die Werte gemäß dem optionalen Argument festlegt. Wenn keines vorhanden ist, wird der Wert für alle Gebietsschemas festgelegt.
% 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}
Ich habe versucht, den Befehl so zu ändern, dass beliebige Gebietsschemas akzeptiert werden, aber etwas funktioniert nicht.
\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}}%
}
Wenn ich die verwendeten Werte einstelle, ist alles in Ordnung. Wenn keine Werte festgelegt sind, funktionieren meine benutzerdefinierten Umgebungen nicht mehr und die angezeigte Standardmeldung lautet nur Use \Cmd[] to...
, also ohne die Gebietsschemanamen.
Irgendeine Idee, was los ist?
Antwort1
Wie im Kommentar erwähnt, war Ihr Hauptproblem, dass das \n
im Ersetzungstext von \@namedef
nicht auf seinen Wert erweitert wurde. Der Ersetzungstext für Faculty:fi
beispielsweise blieb also wörtlich
{\scriptsize (Use {\tt\textbackslash #2[\n]} to replace this text.)}%
mit \n
. In den meisten Kontexten, in denen das Makro aufgerufen würde, \n
wäre es nicht definiert und Sie würden eine Fehlermeldung erhalten. Sie möchten, dass der Makro-Ersetzungstext so erstellt wird, dass \n
seine Erweiterung wird, also fi
oder en
. Der einfachste Weg, dies richtig zu erledigen, ist wahrscheinlich ein Hilfsmakro. Diese Antwort zeigt zwei Ansätze: Einen mit etoolbox
und seinen Listenmakros und einen mit Ihrem Code, der \expandafter
und ein Hilfsmakro verwendet. etoolbox
Die Schleifen von funktionieren, indem der Wert der Schleifenvariable direkt als Argument an ein Hilfsmakro übergeben wird.
Im ersten Argument von \@namedef
(also dem #2:\n
) \n
würde das automatisch erweitert, so dass es auch wirklich dort stünde fi
.
Hier ist eine Lösung, die etoolbox
und ihre Listenmakros verwendet. Einige Erklärungen sind inline. Der Vorteil dieses Ansatzes besteht darin, dass keine Erweiterung der Schleifenvariable ( \n
) erforderlich ist, da die Schleife direkt als Makro implementiert ist (normalerweise ist ein Hilfsmakro erforderlich).
\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}
\foreach
Wenn Sie bei Ihrer Version mit und bleiben möchten \ifthenelse
, können Sie eine Hilfsfunktion verwenden.
Der Trick besteht darin, dass wir erweitern müssen, \n
damit wir den tatsächlichen Wert erhalten. Dies ist mit möglich \expandafter
, aber um nicht mit chirurgischer Präzision viele Token überspringen zu müssen, ist eine Hilfsfunktion nützlich. Da es etwas einfacher ist, das erste Argument eines Makros mit s statt mit einem späteren Argument zu erweitern \expandafter
, verwendet die Hilfsfunktion die etwas unerwartete Argumentreihenfolge {<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}
Übrigens: Ich habe \ttfamily
anstelle von verwendet \tt
, da die zweistelligen Schriftbefehle in LaTeX2e veraltet sind (https://texfaq.org/FAQ-2letterfontcmd,Werden zweibuchstabige Schriftstilbefehle (\bf, \it, …) jemals in LaTeX wiederbelebt?).