
Я хотел бы создать среду, которая немного меняется в зависимости от аргумента, указанного для нее. Причина в наличии разных локалей в документе. Я определил команду, EmitLanguageStr
которая определяет аргумент в otherlanguage
соответствии с переданным аргументом. Например, \EmitLanguageStr[en] -> english
. См. эти вопросы для дальнейшего контекста: [1,2] Вот окружающая среда в сокращенном виде.
\renewenvironment{abstract}[1][en]{%
\section*{\EmitAbstractStr[#1]}
\begin{otherlanguage}{\EmitLanguageStr[#1]}
\@author\par
\textbf{\EmitTitle[#1]}\par
}{%
\end{otherlanguage}
}
Оказывается, у других людей была такая же проблема (В), но решение не совсем сработало для меня. Ответ предполагает, что обертывание окружения одним из двух способов позволит расширить аргумент:
% 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
via \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
и \futurelet
для проверки наличия необязательного аргумента.
Эти токены не исчезнут в чистом контексте расширения, в то время как получение имени языка для передачи его в качестве аргумента в среду было бы чистым контекстом расширения. После расширения эти нерасширяемые токены присваивания остаются в потоке токенов, потому что в то время как расширение происходит в «пищеводе» (La)TeX, выполнение присваиваний происходит в «желудке» (La)TeX.
Итак, вкратце:
Пока \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}