
我想建構一個可用於定義指令的巨集。這些命令的行為類似於變數或變數結構,因此它們可以包含多個值。 “成員”透過可選參數傳遞。我用它來定義模板環境,並為不同的語言提供不同的字串,這些字串需要同時出現在文件中。
這就是我所擁有的和所做的工作。
\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}}%
}
首先設定預設值。然後建立一個命令,根據可選參數設定值。如果沒有,則為所有區域設定設定值。
% 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}
我嘗試修改命令以接受任意區域設置,但有些東西不起作用。
\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}}%
}
如果我設定使用的值,一切都會很好。當未設定值時,我的自訂環境就會中斷,並且顯示的預設訊息只是Use \Cmd[] to...
,因此沒有區域設定名稱。
知道發生了什麼事嗎?
答案1
正如評論中所提到的,您的主要問題是 的\n
替換文字中的\@namedef
未擴展到其值。Faculty:fi
例如,替換文字仍按字面意思保留
{\scriptsize (Use {\tt\textbackslash #2[\n]} to replace this text.)}%
和\n
。在大多數情況下,呼叫巨集的地方\n
都是未定義的,並且您會收到錯誤。您希望構造巨集替換文本,使其\n
成為其擴展,即fi
或en
。正確完成此操作的最簡單方法可能是輔助巨集。這個答案將展示兩種方法:一種使用etoolbox
其清單宏,另一種使用使用\expandafter
輔助宏的程式碼。etoolbox
的循環透過將循環變數的值直接作為參數傳遞給輔助宏來運作。
\@namedef
在(即)的第一個參數中#2:\n
,\n
會自動擴展,以便它真正出現fi
在那裡。
etoolbox
這是使用及其列表巨集的解決方案。有些解釋是內聯的。這種方法的優點是不需要擴展循環變數 ( \n
),因為循環直接實作為巨集(通常需要輔助宏)。
\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
,\ifthenelse
您可以使用輔助函數。
訣竅在於我們需要擴展\n
才能獲得其實際價值。這對於 是可能的\expandafter
,但為了避免必須以外科手術般的精度使用它來跳過許多標記,輔助函數很有用。由於使用 s 而不是後面的參數來擴展巨集的第一個參數稍微容易一些\expandafter
,因此輔助函數使用了稍微出乎意料的參數順序{<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}
順便一提:我使用\ttfamily
而不是\tt
,因為在 LaTeX2e 中不建議使用兩個字母的字體命令(https://texfaq.org/FAQ-2letterfontcmd,兩個字母的字體樣式指令(\bf、\it、...)會在 LaTeX 中復活嗎?)。