% \iffalse meta-comment % % Copyright (C) 2016-2018 by Richard Grewe % ------------------------------------------------------- % % This file may be distributed and/or modified under the % conditions of the LaTeX Project Public License, either version 1.2 % of this license or (at your option) any later version. % The latest version of this license is in: % % http://www.latex-project.org/lppl.txt % % and version 1.2 or later is part of all distributions of LaTeX % version 1999/12/01 or later. % % \fi % % \iffalse %<*driver> \ProvidesFile{multilang.dtx} % %\NeedsTeXFormat{LaTeX2e}[1999/12/01] %\NeedsTeXFormat{LaTeX2e}[1999/12/01] %\NeedsTeXFormat{LaTeX2e}[1999/12/01] %\ProvidesPackage{multilang} %<*package> [2018/11/02 v0.9b Tools for maintaining documents in multiple languages] % %\ProvidesPackage{multilang-tags}[2018/11/02 v0.9b Filtering of multilingual macros by tags] %\ProvidesPackage{multilang-sect}[2018/11/02 v0.9b Multilingual sectioning environments] % %<*driver> \documentclass{ltxdoc} \usepackage[columns=2]{idxlayout} \usepackage{xcolor} \usepackage{xspace} \usepackage[inline]{enumitem} \usepackage{showexpl} \lstset{gobble=2,frame=trbl,backgroundcolor=\color{black!5!white},width=0.475\textwidth} \lstset{basicstyle=\footnotesize\fontfamily{pcr}\selectfont} \lstset{explpreset={columns=fixed,numbers=none,language={}}} \lstset{preset={\small\sffamily},overhang=2cm,pos=r,varwidth=false} \usepackage{pbox} \newcommand\NiceDescribeStuff[2]{% #1=margin text, #2=body text \medskip\par\noindent\leavevmode% \marginpar{\hfill\pbox[t]{2\marginparwidth}{\ttfamily #1}% \hspace*{-\marginparsep}}% \ifstrempty{#2}{}{#2\smallskip\\}} \newcommand\NiceDescribeEnv[3][]{% #1=index list, #2=envname, #3=parameters \NiceDescribeStuff{% \textcolor{gray}{\cs{begin}}\string{#2\string}\\ \textcolor{gray}{\cs{end}}\string{#2\string}}{#3}% \ifstrempty{#1} {\SpecialEnvIndex{#1}}% {\forcsvlist{\SpecialEnvIndex}{#1}}% \ignorespaces} \newcommand\NiceDescribeMacro[2]{% #1=macro, #2=parameters \NiceDescribeStuff{\hbox to 0pt{\hss\string#1}}{#2}% \SpecialUsageIndex{#1}\ignorespaces} \newcommand\NiceDescribeConstant[1]{% #1=constant \NiceDescribeStuff{\hbox to 0pt{\hss #1\quad}}{}\ignorespaces} \makeatletter \newcommand\SaveSecs{% \@for\SC:=section,subsection,subsubsection\do{% \csedef{SC@\SC}{\the\value{\SC}}% \setcounter{\SC}{0}}} \newcommand\RestoreSecs{% \@for\SC:=section,subsection,subsubsection\do{% \setcounter{\SC}{\csuse{SC@\SC}}}} \makeatother \newcommand\ThisPackage{\textsf{multilang}\xspace} \usepackage{hypdoc} \usepackage[capitalise,nameinlink]{cleveref} % the following packages are for the examples \usepackage[german,english]{babel} \usepackage[languages={english,german}]{multilang} \usepackage[autostyle=true]{csquotes} \usepackage[useregional]{datetime2} \usepackage{sectionbox} \usepackage{translations} \usepackage{multilang-tags} \usepackage{multilang-sect} % end of examples packages \EnableCrossrefs \CodelineIndex \RecordChanges \begin{document} \DocInput{multilang.dtx} \PrintChanges \PrintIndex \end{document} % % \fi % % \CheckSum{0} % % \CharacterTable % {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z % Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z % Digits \0\1\2\3\4\5\6\7\8\9 % Exclamation \! Double quote \" Hash (number) \# % Dollar \$ Percent \% Ampersand \& % Acute accent \' Left paren \( Right paren \) % Asterisk \* Plus \+ Comma \, % Minus \- Point \. Solidus \/ % Colon \: Semicolon \; Less than \< % Equals \= Greater than \> Question mark \? % Commercial at \@ Left bracket \[ Backslash \\ % Right bracket \] Circumflex \^ Underscore \_ % Grave accent \` Left brace \{ Vertical bar \| % Right brace \} Tilde \~} % % \changes{v0.9}{2017/08/30}{Initial version} % \changes{v0.9b}{2018/11/02}{Package author's name change} % % \GetFileInfo{multilang.dtx} % % \DoNotIndex{\newcommand,\newenvironment,\def,\gdef,\edef} % % % \title{The \ThisPackage package\thanks{This document % corresponds to \ThisPackage~\fileversion, dated \filedate. % The package is available online at % \url{http://www.ctan.org/pkg/multilang} and % \url{https://github.com/Ri-Ga/multilang}.}} % \author{Richard Grewe \\ \texttt{r-g+tex@posteo.net}} % % \maketitle % % \begin{abstract} % Maintaining a \LaTeX{} document with translations for multiple languages % can be cumbersome and error-prone. % The \ThisPackage package provides a set of macros for defining macros and % environments as wrappers around existing macros and environments. % These wrappers allow one to clearly specify multiple translations for the % arguments to the wrapped macros and environments while only the % translation of the document's language is actually shown. % Choosing a translation then is as simple as choosing the document's % language via \textsf{babel} or \textsf{polyglossia}. % \end{abstract} % % \section{Introduction} % \label{sec:Introduction} % % The main goal of the \ThisPackage package is to facilitate the % definition of macros and environments with which documents can be % provisioned in multiple languages. % To be more concrete, \ThisPackage facilitates % \begin{enumerate} % \item the specification of content translations: % % \begin{itemize}[noitemsep] % \item Arguments to macros are specified by their name such that multiple % translations do not clutter up the \LaTeX{} code that easily. % \item If you forget to specify a mandatory argument in a language, % \ThisPackage shows an error when you compile for that language. % \end{itemize} % % \item the maintenance of translations:\smallskip % % Translations of content that is passed as an argument to a macro can be kept % closely together such that you can keep track of which units belong together % and can keep consistency among translations across content changes. % % \item the selection of a document language:\smallskip % % You simply specify the language with \textsf{babel} or \textsf{polyglossia} % and \ThisPackage selects the translation you provided for the language. % \end{enumerate} % % The motivating example that lead to the development of this package are CVs % (curricula vitae). Suppose you want to maintain a CV document that contains % the union of all your relevant personal data, education, achievements, etc. % You update the CV from time to time with new achievements. Since you want to % prepare for the possibility that you might apply nationally as well as % internationally, you maintain the CV document in multiple languages (e.g., % your mother language and English). % When you use the CV in a concrete job application, you % \begin{enumerate*}[label=(\arabic*)] % \item filter out all details that are irrelevant for the position that you % apply for % and % \item select a suitable language for the employer. % \end{enumerate*} % % This package provides a set of basic macros for the definition of % multilingual macros and multilingual environments. The following example % illustrates such a macro and how it could be used. % \SaveSecs % % \begin{LTXexample}[preset={\newcommand\Sec[1]{\section{Foobar}}}] % % ... % \usepackage[german,english]{babel} % \usepackage[languages={english,german}] % {multilang} % % ... % \begin{document} % \Sec{ % title/english={Foobar}, % title/german ={Dingsda} % } % ... % \end{document} % \end{LTXexample} % \RestoreSecs % \Cref{sec:Usage} describes how a macro such as |\Sec| in the above % example can be defined with \ThisPackage. The \lcnamecref{sec:Usage} also % describes how analogous environments can be defined. % \Cref{sec:Types} describes extensible argument types. % \Cref{sec:Extensions} describes two extension packages, % \textsf{multilang-sect} and \textsf{multilang-tags} for multilingual % sectioning and, respectively, for filtering multilingual macros and % environments. % % % \section{Usage} % \label{sec:Usage} % % \subsection{Package Options} % % \NiceDescribeConstant{languages} % The \ThisPackage package has a single option: |languages|. This option % expects a comma-separated list of language names. The ordering of the list % does not make a difference. Through this option, one sets the languages for % which translations can be provided. By default, the list is empty such that % no translations can be specified. % The example in \cref{sec:Introduction} demonstrates how this option can be % used. % % % \subsection{Multilingual Macros} % \label{sec:Usage-Macros} % % \NiceDescribeMacro{\NewMultilangCmd}{\marg{command}\marg{options}} % A multilingual macro can be defined via the |\NewMultilangCmd| macro. % The macro defines \meta{command} as a macro that accepts a single argument. % That is, the signature of \meta{command} is \meta{command}\marg{kvarg}. The % argument, \meta{kvarg}, is a comma-separated key-value list. % The \meta{options} argument takes the form of a comma-separated key-value % list and specifies what \meta{command} does, including how \meta{kvarg} is % interpreted. The following keys are defined for \meta{options}: % \begin{description}[noitemsep] % \item[|command|:] % This key is to be used in the form ``|command=|\meta{c}'', where \meta{c} % is an already existing command. % The key specifies that \meta{command} is defined to invoke \meta{c} with % some arguments. % Example: see \cref{sec:Usage-margs}. % \item[|margs|:] % This key is to be used in the form ``|margs={|\meta{csvlist}|}|'', where % \meta{csvlist} is a comma-separated list of names. % The key specifies names for the mandatory arguments of \meta{c}, in the % order specified. % If \meta{c} is a macro that takes $n$ mandatory arguments, then % \meta{csvlist} should be a list of $n$ names. If \meta{arg-i} is the % $i$-th entry in \meta{csvlist}, then the $i$-th mandatory % argument to \meta{c} can be specified as \meta{v} in the invocation of % \meta{command} as follows: % ``\meta{command}|{...,|\meta{arg-i}|=|\meta{v}|,...}|''. % Example: see \cref{sec:Usage-margs}. % \item[|oargs|:] % This key is to be used in the form ``|oargs={|\meta{csvlist}|}|'', where % \meta{csvlist} is a comma-separated list of names. % This key is analogous to |margs|, but for optional arguments to \meta{c}. % Example: see \cref{sec:Usage-oargs}. % \item[|starred|:] % This key is to be used simply as % ``|starred=|\meta{bool}'', where \meta{bool} must be |true| or |false|, % or just as ``|starred|'', which is equivalent to ``|starred=true|''. % If \meta{bool} is |true|, then ``\meta{command}|*{...}|'' can be used to % invoke ``\meta{c}|*...|''. % Example: see \cref{sec:Usage-starred}. % \item[|disablable|:] % This key is to be used simply as % ``|disablable=|\meta{bool}'', where \meta{bool} must be |true| or |false|, % or just as ``|disablable|'', which is equivalent to ``|disablable=true|''. % If \meta{bool} is |true|, then the invocation of \meta{c} in % \meta{command} can be suppressed via % ``\meta{command}|{...,disabled,...}|''. % Example: see \cref{sec:Usage-disablable}. % \item[|defaults|:] % This key is to be used as % ``|defaults={...,|\meta{arg}|=|\meta{value}|,...}|''. % Each key \meta{arg} should be the name of an optional or mandatory % argument and \meta{value} should be the intended default value for this % argument. % Example: see \cref{sec:Usage-defaults}. % \item[|alias/...|:] % This key is to be used as ``|alias/|\meta{name}|={|\meta{arglist}|}|'', % where \meta{name} specifies the name of the alias and \meta{arglist} is a % possibly empty comma-separated list of argument names (optional or % mandatory arguments allowed). % Example: see \cref{sec:Usage-alias}. % \end{description} % The remainder of \cref{sec:Usage-Macros} provides examples based on the % |\section| macro. % % % \subsubsection{Basic Usage} % \label{sec:Usage-margs} % The following example shows how the |\Section| macro used in the example of % \cref{sec:Introduction} can be defined. We leave out the \textsf{babel} code % in this \lcnamecref{sec:Usage} for more concise example code. % % \SaveSecs % \begin{LTXexample}[morekeywords={NewMultilangCmd,command,margs}] % \NewMultilangCmd{\Sec}{ % command=\section, margs=title} % \Sec{ % title/english={Foobar}, % title/german ={Dingsda} % } % \end{LTXexample} % \RestoreSecs % % % \paragraph{Language-independent arguments.} % Sometimes, arguments to macros need no translation. For instance, if an % argument is a technical term or a name, it can remain in the original % language. To avoid redundancy, in such instances the language part of a % mandatory or optional argument can be omitted to specify the argument for % all languages. % % \SaveSecs % \begin{LTXexample}[preset={\NewMultilangCmd{\Sec}{margs=title,oargs=short,command=\section}}] % \Sec{title=multilang} % \end{LTXexample} % \RestoreSecs % % % \paragraph{Forced foreign-language arguments.} % \label{sec:Usage-forced-language} % A particular instance of a language-independent argument is the case in which % the argument shall explicitly be typeset in a particular language. For % instance, the argument might use macros that internally determine the % display based on the selected language. The following example shows the case % of forcing German display of the |\enquote| macro. % % \SaveSecs % \begin{LTXexample}[preset={\NewMultilangCmd{\Sec}{margs=title,oargs=short,command=\section}}] % \usepackage[autostyle=true]{csquotes} % %... % \Sec{title/german!=\enquote{multilang}} % \end{LTXexample} % \RestoreSecs % % % \subsubsection{Optional Arguments} % \label{sec:Usage-oargs} % Macros like |\section| don't just have a mandatory argument (the section % title) but also an optional argument (a short title for the table of % contents). We can make this optional argument accessible via the |oargs| % option as follows. % The displayed result does not show any difference as we don't have the table % of contents here. % % \SaveSecs % \begin{LTXexample}[morekeywords={oargs}] % \NewMultilangCmd{\Sec}{ % command=\section, margs=title, oargs=short} % \Sec{ % title/english={Foobar}, % title/german ={Dingsda}, % short/english={F}, % short/german ={D}, % } % \end{LTXexample} % \RestoreSecs % % % \subsubsection{Starred Macros} % \label{sec:Usage-starred} % Some macros can be altered in their behavior with a star (``|*|'') after the % macro. The |\section| command is an example of such a macro: |\section*| % suppresses the display of the section number. The star can be transferred to % the command defined via |\NewMultilangCmd| as the following example % illustrates. Note the exclamation mark after ``|german|''. % % \SaveSecs % \begin{LTXexample}[morekeywords={starred}] % \NewMultilangCmd{\Sec}{starred, % command=\section, margs=title, oargs=short} % \Sec{title=Foo} % \Sec*{title=Bar} % \end{LTXexample} % \RestoreSecs % % % \subsubsection{Disabling Display} % \label{sec:Usage-disablable} % When a macro is defined with the |disablable| option, it can be passed the % |disabled| argument which disables the display of the macro. One might % consider this a nicer way to disable content than commenting out the macro. % % \SaveSecs % \begin{LTXexample}[morekeywords={disablable}] % \NewMultilangCmd{\Sec}{disablable, % command=\section, margs=title, oargs=short} % \Sec{title=Foo} % \Sec{title=Bar,disabled} % \end{LTXexample} % \RestoreSecs % % % \subsubsection{Default Arguments} % \label{sec:Usage-defaults} % When a mandatory or optional argument shall by default assume a particular % value if no value is specified for the argument in the argument to % \meta{command}, this can be specified via the |defaults| key as the % following example demonstrates. % % \SaveSecs % \begin{LTXexample}[morekeywords={defaults}] % \NewMultilangCmd{\Sec}{ % command=\section, margs=title, % defaults={title={???}}} % \Sec{} % \end{LTXexample} % \RestoreSecs % Defaults can be particularly useful for mandatory arguments of commands % that take multiple arguments and that can often be left empty. % Essentially, argument defaults turn mandatory arguments of % macros to optional arguments. % % % \subsubsection{Argument Aliases} % \label{sec:Usage-alias} % Aliases allow one to specify argument names of three different kinds: % \begin{description}[nosep] % \item[direct aliases:] % A direct alias declares an argument name that can then be used as a % substitute for some mandatory or optional argument. % The |heading| argument in the following example shows how direct aliases % can be declared. % \item[combiner aliases:] % A combiner alias declares an argument name that acts as a substitute for a % sequence of mandatory or optional arguments. % Such aliases particularly help maintaining concise \LaTeX{} source code % when argument values are rather short, as |both| in the following example % shows. % \item[comment aliases:] % A comment alias declares an argument name that represents no mandatory or % optional argument. That is, a value specified for a comment alias is not % used as an argument to the |command| and can, hence, be used for capturing % comments or values for future use. % \end{description} % % \SaveSecs % \begin{LTXexample}[morekeywords={alias}] % \NewMultilangCmd{\Sec}{ % command=\section, margs=title, oargs=short, % alias/heading=title, % alias/both={title,short}, % alias/remark} % \Sec{ % both/english={Foobar}{Foo}, % both/german ={Dingsda}{Dings}} % \Sec{ % heading=Baz, % remark={select heading+translate}} % \end{LTXexample} % \RestoreSecs % % % \subsection{Multilingual Environments} % % \NiceDescribeMacro{\NewMultilangEnv}{\marg{environment}\marg{options}} % The usage of the |\NewMultilangEnv| macro is analogous to the usage of % the macro |\NewMultilangCmd|, except for the following differences: % \begin{itemize}[noitemsep] % \item The first argument, \meta{environment}, expects the name of an % environment that shall be defined. % \item In the \meta{options}, the |environment| key substitutes the |command| % key and expects an environment name. % \item The |starred| key is not available. Following standard \LaTeX{} % practices, if you want to define a starred environment, simply use the % starred name for \meta{environment}. % \end{itemize} % Due to the similarity to |\NewMultilangCmd|, we don't provide separate % examples for all the individual features. % Continuing the line of examples started before, we again use an example % about sections -- just now with an environment for section boxes, as % provided by the \textsf{sectionbox} package. % % \SaveSecs % \begin{LTXexample}[morekeywords={NewMultilangEnv}] % \usepackage{sectionbox} % \NewMultilangEnv{SecBox}{ % environment=sectionbox, % disablable, margs=title, oargs=width} % % \begin{SecBox}{title=Foo} % content % \end{SecBox} % % \begin{SecBox}{disabled,title=Bar} % disabled content % \end{SecBox} % \end{LTXexample} % \RestoreSecs % % % \section{Extensible Argument Types} % \label{sec:Types} % % By default, an argument |arg| in a multilingual macro can be specified in % three ways: just ``|arg=...|'', ``|arg/|\meta{language}|=...|'', or % ``|arg/|\meta{language}|!=...|''. % The \ThisPackage package enables one to declare further so called `types'. % These types can be used in place of \meta{language} in the argument syntax. % They can be used for uniformly enabling a special formatting if an argument % is of a particular type (rather than just being text). % % \NiceDescribeMacro{\NewMultilangType}{\oarg{argcount}\marg{typename}\marg{format}} % The |\NewMultilangType| macro declares the \meta{typename} type. % Values passed to arguments of this type must consist of \meta{argcount} % arguments (default: 1). The value is then formatted with \meta{format} when % displayed. The \meta{format} can (and should) contain positional parameters % such as ``|#1|'' for the first argument. % The remainder of this \lcnamecref{sec:Types} demonstrates the use of % the macro by examples. % % % \subsection{Dates via \textsf{datetime2}} % % The \textsf{datetime2} package supports regional (language-specific) % formatting of dates. We can build on this feature such that we don't % have to provide translations of dates. To keep the display % smaller, we here use |\textbf| rather than |\section|. % % \begin{LTXexample}[morekeywords={NewMultilangType,date,daterange}] % \usepackage[useregional]{datetime2} % % \NewMultilangType{date}{\DTMdate{#1}} % \NewMultilangType[2]{daterange} % {\DTMdate{#1}--\DTMdate{#2}} % % \NewMultilangCmd{\Bold} % {margs=title, command=\textbf} % % Date: \Bold{title/date={2017-08-01}}\\ % Range: \Bold{title/daterange= % {2017-01-01}{2017-08-01}} % \end{LTXexample} % % % \subsection{Nesting Multilingual Macros} % % Multilingual macros can be used in arguments to multilingual macros. % Coming back to the original motivation for developing \ThisPackage % -- CVs -- you might want a translated two-column layout in which the % right column might be filled with translated list items. The following % example shows how this can be realized. % % \begin{LTXexample} % \usepackage{enumitem}% for "nosep" % % \newcommand\entry[2]{% % \begin{tabular}{p{1cm}p{4cm}}#1 % \end{tabular}} % % \NewMultilangType{list}{% % \begin{minipage}[t]{4cm} % \begin{itemize}[nosep]#1 % \end{itemize}\end{minipage}} % \NewMultilangCmd{\Entry} % {margs={head,text}, command=\entry} % \NewMultilangCmd{\Item} % {margs=name, command=\item} % % \Entry{head=2017, % text/list={ % \Item{name/english=foobar, % name/german =Dingsda} % \Item{name=multilang}}} % \end{LTXexample} % % % \section{Extension Packages} % \label{sec:Extensions} % % The \ThisPackage package comes bundled with a few generic packages that build % on \ThisPackage. These packages are described below. % % \subsection{Sectioning Environments} % % Sectioning environments are provided by the \textsf{multilang-sect} package. % That package defines, for each of \LaTeX's sectioning macros (|\section|, % \ldots, |\subparagraph|) an environment and a starred environment. % % \NiceDescribeEnv[Section,Section*]{Section(*)}{\marg{data}} % This environment shows a section. % It has a single, mandatory argument, named |title|. % It is a disablable environment, i.e., the argument |disabled| can be used in % \meta{data} to disable the display of the whole section. % This environment acts as a proxy for the |\section| macro as it is used by % \ThisPackage (i.e., without optional argument and without the star). % \NiceDescribeEnv[SubSection,SubSection*]{SubSection(*)}{\marg{data}} % This environment is analogous to the |Section| environment, just for % sub-sections. % \NiceDescribeEnv[SubSubSection,SubSubSection*]{SubSubSection(*)}{\marg{data}} % This environment is analogous to the |Section| environment, just for % sub-sub-sections. % \NiceDescribeEnv[Paragraph,Paragraph*]{Paragraph(*)}{\marg{data}} % This environment is analogous to the |Section| environment, just for % paragraphs. % \NiceDescribeEnv[SubParagraph,SubParagraph*]{SubParagraph(*)}{\marg{data}} % This environment is analogous to the |Section| environment, just for % sub-paragraphs. % Examples: % % \SaveSecs % \begin{LTXexample}[morekeywords={Section,SubSection}] % \usepackage{multilang-sect} % % \begin{Section}{ % title/english = Usage, % title/german = Benutzung, % } % (section content) % \begin{SubSection*}{ % title/english = Package Options, % title/german = Paketoptionen, % } % (subsection content) % \end{SubSection*} % \end{Section} % \end{LTXexample} % \RestoreSecs % % % \subsection{Tags} % % Tags are an alternative to individually disabling macros or environments. % They are provided by the \textsf{multilang-tags} package. % A tag is just a word and sets of tags can be assigned to individual usages of % multilingual macros and environments. % As long as no tag filter policy is setup, specifying tags does not influence % what is displayed. % % \NiceDescribeMacro{\SetTagFilter}{\oarg{default}\marg{policy}} % The |\SetTagFilter|\oarg{default}\marg{policy} sets up a tag filter policy. % The \meta{policy} is a comma-separated list of |accept|/|deny| rules. % The \meta{default} argument is either |accept| (the default) or |deny| and % specifies the default policy. % The following toy example demonstrates tag filtering: % % \begin{LTXexample}[morekeywords={SetTagFilter,tags}] % \usepackage{multilang-tags} % \NewMultilangCmd{\Item}{disablable, % command=\item,oargs=dd,margs=dt, % alias/both={dd,dt}} % \SetTagFilter{accept={A}, deny={D}} % % \begin{description} % \Item{tags=A, both={1}{tagged A}} % \Item{tags={A,D}, both={2}{tagged A,D}} % \Item{tags=D, both={3}{tagged D}} % \Item{tags={D,X}, both={4}{tagged D,X}} % \Item{tags=!D, both={5}{tagged !D}} % \Item{tags=X, both={6}{tagged X}} % \end{description} % \end{LTXexample} % % When a multilingual macro, such as |\BasicEntry| in the above example, is % used, whether the macro content is displayed is determined as follows: % \begin{itemize}[nosep] % \item If no |tags| are specified or no tag policy is setup, then the macro % content is displayed. % \item Otherwise, the rules of the tag policy are processed in sequential order % until the specified |tags| match a rule. % A |tags| list matches a rule if at least one tag in the |tags| occurs in the % rule (with or without ``|!|'' prefix). % Let $t$ be the last tag in |tags| that occurs in the rule. % \begin{itemize}[nosep] % \item The macro content is displayed if the rule is an |accept| rule and $t$ % is not prefixed with ``|!|'', or if the rule is a |deny| rule and $t$ is % prefixed with ``|!|''. % \item Otherwise the display of the macro content is disabled. % \end{itemize} % \item If the specified |tags| match no rule in the \meta{policy}, then the % macro content is displayed if and only if the \meta{default} is |accept|. % \end{itemize} % % Rather than directly setting up a filter policy, one can also use the % following macros to first define filter policies and then select one for use. % % \NiceDescribeMacro{\DefineTagFilter}{\marg{name}\marg{default}\marg{policy}} % This macro defines a tag filter policy with name \meta{name} to represent the % given \meta{policy} and \meta{default}. % % \NiceDescribeMacro{\UseTagFilter}{\marg{name}} % This macro uses the tag filter policy with name \meta{name}. % % Further examples: % \begin{LTXexample}[morekeywords={DefineTagFilter,UseTagFilter,tags},preset={\NewMultilangCmd{\Item}{disablable,command=\item,oargs=dd,margs=dt,alias/both={dd,dt}}}] % \DefineTagFilter{Show}{accept}{} % \DefineTagFilter{Hide}{deny}{} % \DefineTagFilter{OnlyA}{accept}{accept=A, % deny={D,X}} % \begin{description} % \UseTagFilter{Hide} % \Item{ both={1}{no tag}} % \Item{tags=D, both={2}{tagged D}} % \UseTagFilter{OnlyA} % \Item{tags={D,!X},both={3}{tagged D,!X}} % \Item{tags={!X,D},both={4}{tagged !X,D}} % \end{description} % \end{LTXexample} % % % \section{Questions and Answers} % % \begin{description} % \item[Can't I achieve the same thing simpler?] % To some extent, you can. % A variety of ad-hoc solutions to managing translations in \LaTeX{} % documents exist.\footnote{see, e.g., % \url{https://tex.stackexchange.com/q/5076}} % An obvious approach is the following: % \SaveSecs % \begin{LTXexample} % \newcommand\inEnglish[1]{#1} % \newcommand\inGerman[1]{} % % \inEnglish{\section{Foobar}} % \inGerman{\section{Dingsda}} % % or % \section{\inEnglish{Foobar} % \inGerman{Dingsda}} % \end{LTXexample} % \RestoreSecs % That is, for each translation language you define a macro with one % argument; the macro for the ``selected'' language expands to the argument % while all other macros have an empty expansion. % An obvious advantage of this approach over \ThisPackage is that it does % not require learning how to use \ThisPackage. % However, in my opinion, the approach has the following disadvantages: % \begin{itemize}[nosep] % \item Both the first variant and the second variant in the example make % the code more difficult to read due to the nesting of macros and the % curly braces. % \item The first variant even requires the |\section| macro to be repeated % for each language. % \item The second variant easily gets chaotic if instead of |\section| a % macro with several arguments is used. % \end{itemize} % Solutions with conditionals (e.g., |\ifEnglish ...\else ...\fi|) share the % disadvantages of above approach (except the curly braces) and additionally % have an inherent asymmetry that becomes particularly apparent if more than % two languages are involved. % Similar arguments apply to other ad-hoc solutions I have seen. % That is, I find documents based on such approaches cumbersome to maintain % and, hence, requiring more careful checks for ensuring consistency. % % \item[Can I use \ThisPackage with \textsf{polyglossia} instead of \textsf{babel}?] % Yes, you can use either one for selecting the language of your document. % % \item[Can I switch the language mid-document?] % You can switch the language mid-document (e.g., using Babel's % |selectlanguage| environment), but this does not effect what the % multilingual macros or environments defined via \ThisPackage. % The language that is displayed by a macro or environment is determined at % the time of loading \ThisPackage. % Future versions of \ThisPackage might add support for switching languages % mid-document, though. % % \item[Are language dialects supported?] % No, currently they are not supported. % % \item[Can I store the ``result'' of a multilingual macro in another macro?] % No. % You can store the macro itself (e.g., via |\newcommand|), but storing the % result of the multilingual macro in a macro (e.g., via |\edef|) is not % possible, as the multilingual macros are not expandable. % \end{description} % % % \section{Related Packages} % % I'm not aware of any \LaTeX{} packages that pursue similar goals or % provide similar functionality. % CTAN provides a list of many packages for supporting more than one % language.\footnote{\url{https://www.ctan.org/topic/multilingual}} % In the following, we compare against some of these packages. % % \begin{description} % \item[\textsf{babel}, \textsf{polyglossia}:] % These package provide support for selecting a document language and % switching the document language within a document. The selected language % is then used for hyphenation and other layouting aspects. % Providing multiple translations of pieces of content is not particularly % facilitated by the two packages. % % The \ThisPackage package builds on \textsf{babel} or \textsf{polyglossia} % for determining the selected language and for forced foreign-language % formatting (as illustrated in \cref{sec:Usage-margs}). % % \item[\textsf{translations}:] % This package aims primarily aims at package authors, providing them an % easy interface for providing translations of package-specific terms. % Essentially, one declares translations for terms up-front and then later % can use these translations. % % The separation of translations and the use of terms is beneficial % for the maintenance of packages and documents in which individual terms % occur multiple times. However, I assume that this separation would make it % harder to maintain documents in which most translated units occur only % once and at a particular location in the document. % % One could combine the virtues of \textsf{translations} and \ThisPackage as % follows: % \SaveSecs % \begin{LTXexample}[morekeywords={DeclareTranslation,GetTranslation},preset={\let\DeclareTranslation=\addtranslation}] % % in preamble % \usepackage{translations} % \DeclareTranslation{english}{foo}{Foobar} % \DeclareTranslation{german}{foo}{Dingsda} % \NewMultilangType{translate} % {\GetTranslation{#1}} % \NewMultilangCmd{\Sec}{ % margs=title, command=\section} % % % in document % \Sec{title/translate=foo} % \end{LTXexample} % \RestoreSecs % % \item[\textsf{xt\_capts}:] % This package is similar to the \textsf{translations} package, even though % the package's documentation does not explicitly refer to package authors % as the target users. That is, it provides commands for declaring and using % translations of terms. % The main difference to \textsf{translations}, as far as I understand, is % that the user interface of \textsf{translations} is larger and supports % language dialects. % Comparing with \ThisPackage, the same remarks as for \textsf{translations} % apply. % \end{description} % % % \clearpage % % \StopEventually{} %\iffalse %<*package> %\fi % % % \section{Implementation} % % \subsection{Dependencies} % % We use \textsf{pgfkeys} for the options parsing, both for the macros defined % by \ThisPackage and for the macro defined via the macros in \ThisPackage. % We use \textsf{etoolbox} to simplify the internal code. % \begin{macrocode} \RequirePackage{pgfkeys,pgfopts} \RequirePackage{etoolbox} % \end{macrocode} % We use the \textsf{environ} package for scanning and later forgetting the % body of disabled multilingual environments. % \begin{macrocode} \RequirePackage{environ} % \end{macrocode} % % % \subsection{Package Options} % % The ``|languages|'' option selects the languages that \ThisPackage knows % about. % \begin{macrocode} \newcommand\multilang@@langs{} \pgfqkeys{/multilang/pkg}{ languages/.code={\forcsvlist{\listadd\multilang@@langs}{#1}}, } \ProcessPgfOptions{/multilang/pkg} % \end{macrocode} % % \subsection{Main Macros} % % \begin{macro}{\NewMultilangCmd} % The |\NewMultilangCmd|\marg{command}\marg{options} macro defines % \meta{command} to be a single-argument macro. The argument to \meta{command} % specifies, in a key-value list style, the mandatory and optional arguments % that are passed to a command specified in \meta{options}. % \begin{macrocode} \newcommand\NewMultilangCmd[2]{% \bgroup % \end{macrocode} % The following line processes the \meta{options} given and, as its result, % defines the macros % |\multilang@@actuals|, |\multilang@@checks|, and |\multilang@@keys|. % \begin{macrocode} \multilang@processargs{#1}{/multilang/newcommand}{defaults={},#2}% % \end{macrocode} % To handle starred macros, we store the actual macro code into an auxiliary % macro and define \meta{command} to be an interface to the auxiliary macro. % The next line stores the name of the internal macro. % \begin{macrocode} \expandafter\def\expandafter\multilang@@intcmd\expandafter{% \csname multilang@intcmd@\expandafter\@gobble\string#1\endcsname}% % \end{macrocode} % Finally, we create the \meta{command}. The |\edef| starting with the % |\egroup| shall expand the three macros constructed above but nothing else % such that none of the |\pgfqkeys| result for |#1| spills outside the % |\NewMultilangCmd|. % \begin{macrocode} \edef\do{\egroup \expandonce{\multilang@@keys}% \ifbool{multilang@@starred}{% % \end{macrocode} % The following handles the case of a |starred| macro. We define the macro % that scans for the star (\meta{command}) as well as the internal macro that % does the actual work. % \begin{macrocode} \unexpanded{\newcommand#1}{% \noexpand\@ifstar {\expandonce{\multilang@@intcmd}{*}}% {\expandonce{\multilang@@intcmd}{}}}% }{% % \end{macrocode} % The following handles the case of a non-|starred| macro. Here we make % \meta{command} directly resort to the internal macro. % \begin{macrocode} \unexpanded{\newcommand#1}{\expandonce{\multilang@@intcmd}{}}% }% % \end{macrocode} % The remainder of the macro code defines the internal macro, with signature % \meta{\cs{multilang@@keys}}\marg{decoration}\marg{kvarg}. % The \meta{decoration} can assume any symbols that shall directly be put % after the command encapsulated by |command|. A particular use for the % \meta{decoration} argument is the ``|*|'' symbol for a |starred| macro. % \begin{macrocode} \noexpand\newcommand{\expandonce{\multilang@@intcmd}}[2]{% % \end{macrocode} % First, \meta{command} parses its argument using \textsf{pgfkeys}. % \begin{macrocode} \bgroup \noexpand\boolfalse{multilang@cmd@@disabled}% \noexpand\pgfqkeys{\multilang@keyof{#1}}{% \expandonce{\multilang@@defaults},####2}% \noexpand\ifbool{multilang@cmd@@disabled}% % \end{macrocode} % If the macro is disabled, simply use an empty invocation % |\multilang@@invok|. % \begin{macrocode} {\unexpanded{\def\multilang@@invok{}}}% % \end{macrocode} % Otherwise, first check the arguments and afterwards define % |\multilang@@invok| to contain \meta{command}, \meta{decoration} (in % |####1|), and the actual arguments (in |\multilang@@actuals|). % \begin{macrocode} {\expandonce{\multilang@@checks}% \unexpanded{\edef\multilang@@invok}{% \noexpand\unexpanded{\expandonce{\multilang@@cmd}}####1% \expandonce{\multilang@@actuals}}}% % \end{macrocode} % Finally, \meta{command} invokes the command specified via the |command| key. % The invocation happens outside the local group, just for the case that makes % a difference with the |command|. % \begin{macrocode} \unexpanded{\expandafter\egroup\multilang@@invok}% }% }\do} % \end{macrocode} % % % \begin{macro}{\NewMultilangEnv} % The |\NewMultilangEnv|\marg{environment}\marg{options} macro defines % \meta{environment} to be a single-argument environment. The argument to % \meta{environment} specifies, in a key-value list style, the mandatory and % optional arguments that are passed to an environment specified in % \meta{options}. % \begin{macrocode} \newcommand\NewMultilangEnv[2]{% \bgroup % \end{macrocode} % The following line processes the \meta{options} given and, as its result, % defines the macros % |\multilang@@actuals|, |\multilang@@checks|, and |\multilang@@keys|. % \begin{macrocode} \multilang@processargs{#1}{/multilang/newenvir}{defaults={},#2}% % \end{macrocode} % Finally, we create the \meta{environment}. The |\edef| starting with the % |\egroup| shall expand the three macros constructed above but nothing else % such that none of the |\pgfqkeys| result for |#1| spills outside the % |\NewMultilangEnv|. We also pay attention that as few as possible internal % macros spill into the environment or even the code that begins the % environment. % \begin{macrocode} \edef\do{\egroup \expandonce{\multilang@@keys}% \unexpanded{\newenvironment{#1}}[1]{% % \end{macrocode} % First, \meta{command} parses its argument using \textsf{pgfkeys}. % \begin{macrocode} \bgroup \noexpand\boolfalse{multilang@cmd@@disabled}% \noexpand\pgfqkeys{\multilang@keyof{#1}}{####1}% \noexpand\ifbool{multilang@cmd@@disabled}% % \end{macrocode} % If the body shall be disabled, then we don't perform checks on the % arguments, don't open |environment| but rather collect the body of the % environment and finally also ignore the code for closing |environment| (via % |\multilang@noend|). Through this trick, we avoid defining an additional % macro for switching in the end-block of the environment. % \begin{macrocode} {\unexpanded{% \def\multilang@@invok{\Collect@Body{\multilang@noend}}}}% % \end{macrocode} % First check the arguments and afterwards define % |\multilang@@invok| to contain the opening code for the environment, % including the actual arguments (in |\multilang@@actuals|). % \begin{macrocode} {\expandonce{\multilang@@checks}% \unexpanded{\edef\multilang@@invok}{% \noexpand\noexpand\noexpand\begin{\multilang@@env}% \expandonce{\multilang@@actuals}}}% % \end{macrocode} % Finally, \meta{environment} begins the environment specified via the % |environment| key. This happens outside the local group, such that the % internal macros set via |\pgfqkeys| are not visible anymore when the % environment is started. % \begin{macrocode} \unexpanded{\expandafter\egroup\multilang@@invok}% }{% % \end{macrocode} % The following implements the closing of the environment. % \begin{macrocode} \noexpand\end{\multilang@@env}% }% }\do} % \end{macrocode} % % % \subsubsection{Option Keys} % % We first setup the shared keys for the \meta{options} argument of % |\NewMultilangCmd| and |\NewMultilangEnv|. % \begin{macrocode} \pgfqkeys{/multilang/cmd-or-env}{ margs/.store in={\multilang@@margs}, oargs/.store in={\multilang@@oargs}, alias/.is family, alias/.unknown/.code={% \listeadd{\multilang@@aliases}{\pgfkeyscurrentname}% \csdef{multilang@@alias@\pgfkeyscurrentname}{#1}}, defaults/.store in={\multilang@@defaults}, disablable/.is if={multilang@@disablable}, } \newbool{multilang@@disablable} \newbool{multilang@cmd@@disabled} % \end{macrocode} % % Next, we setup the specific keys for |\NewMultilangCmd|. % \begin{macrocode} \pgfqkeys{/multilang/newcommand}{ .search also={/multilang/cmd-or-env}, command/.store in={\multilang@@cmd}, starred/.is if={multilang@@starred}, alias/.search also={/multilang/cmd-or-env}, } \newbool{multilang@@starred} % \end{macrocode} % \end{macro} % Finally, the specific keys for |\NewMultilangEnv|. % \begin{macrocode} \pgfqkeys{/multilang/newenvir}{ .search also={/multilang/cmd-or-env}, environment/.store in={\multilang@@env}, alias/.search also={/multilang/cmd-or-env}, } % \end{macrocode} % \end{macro} % % % \subsection{Registration of Datatypes} % % \begin{macro}{\NewMultilangType} % The |\NewMultilangType|\oarg{argcount}\marg{typename}\marg{format} macro % registers the name \meta{typename} as a type that can be used for specifying % \ThisPackage arguments. The type has \meta{argcount} arguments (default: 1) % and is formatted according to code \meta{format}. % Note that the definition of types is group-local. That is, if % |\NewMultilangType| is used within a group, \meta{typename} is only % available inside that group. % \begin{macrocode} \newcommand\NewMultilangType[3][1]{% % \end{macrocode} % We first record the new type's name (in |\multilang@@types|) and store both % \meta{argcount} and \meta{format} in macros. % \begin{macrocode} \listadd\multilang@@types{#2}% \expandafter\newcommand\csname multilang@@typecmd@#2\endcsname[#1]{#3}% \csdef{multilang@@typeargc@#2}{#1}% % \end{macrocode} % Finally, we also store the invocation of the \meta{format} code macro. % This is a bit cumbersome, as for all possible argument counts we provide the % respective number of arguments. I did not yet find a more elegant way to % achieve that the |style n args| code in |\multilang@regfieldtype| invokes % \meta{format} properly with \meta{argcount} arguments. % \meta{argcount} and \meta{format} in macros. % \begin{macrocode} \ifcase#1\relax \csdef{multilang@@runcmd@#2}{\csuse{multilang@@typecmd@#2}}% % \end{macrocode} % For one argument, we check a special case: \meta{format} is the identity % function. In this case, we directly expand to the argument itself, i.e., we % unfold \meta{format}. We do this such that emptiness of optional arguments % can be checked by a |command| or |environment| without having to expand the % \meta{format} (which might not work out if \meta{format} is not expansible). % For instance, KOMA's |\section| macro hides the TOC entry for % |\section[]{...}| but not for |\section[\X]{...}| even if |\X| expands to an % empty result. % \begin{macrocode} \or\ifcsequal{multilang@@typecmd@#2}{@firstofone}% {\csdef{multilang@@runcmd@#2}{####1}}% {\csdef{multilang@@runcmd@#2}{\csuse{multilang@@typecmd@#2}% {####1}}}% \or\csdef{multilang@@runcmd@#2}{\csuse{multilang@@typecmd@#2}% {####1}{####2}}% \or\csdef{multilang@@runcmd@#2}{\csuse{multilang@@typecmd@#2}% {####1}{####2}{####3}}% \or\csdef{multilang@@runcmd@#2}{\csuse{multilang@@typecmd@#2}% {####1}{####2}{####3}{####4}}% \or\csdef{multilang@@runcmd@#2}{\csuse{multilang@@typecmd@#2}% {####1}{####2}{####3}{####4}{####5}}% \or\csdef{multilang@@runcmd@#2}{\csuse{multilang@@typecmd@#2}% {####1}{####2}{####3}{####4}{####5}{####6}}% \or\csdef{multilang@@runcmd@#2}{\csuse{multilang@@typecmd@#2}% {####1}{####2}{####3}{####4}{####5}{####6}{####7}}% \or\csdef{multilang@@runcmd@#2}{\csuse{multilang@@typecmd@#2}% {####1}{####2}{####3}{####4}{####5}{####6}{####7}{####8}}% \or\csdef{multilang@@runcmd@#2}{\csuse{multilang@@typecmd@#2}% {####1}{####2}{####3}{####4}{####5}{####6}{####7}{####8}{####9}}% \else\multilang@error{Argument count expected to be between 0 and 9, % but is '#1'}\fi} % \end{macrocode} % \end{macro} % % \begin{macro}{\multilang@@types} % The |\multilang@@types| macro collects and holds an \textsf{etoolbox} list % of datatypes defined via |\NewMultilangType|. % \begin{macrocode} \newcommand\multilang@@types{} % \end{macrocode} % \end{macro} % % \begin{macro}{\NewMultilangType@code} % The |\NewMultilangType@code|\oarg{argcount}\marg{typename}\marg{format} % macro is an internal counterpart to |\NewMultilangType| with which not % not the |.style| but the |.code| property of the \meta{typename} key is % defined. This is indicated by defining the % |\multilang@@codetype@|\meta{typename} macro here and checking whether this % macro is defined in |\multilang@regfieldtype|. % \begin{macrocode} \newcommand\NewMultilangType@code[3][1]{% \csdef{multilang@@codetype@#2}{true}% \NewMultilangType[#1]{#2}{#3}} % \end{macrocode} % \end{macro} % % \begin{macro}{\multilang@regfield} % The |\multilang@regfield|\marg{cmd-or-env}\marg{fieldname} macro registers % the respective \textsf{pgfkeys} keys for \meta{fieldname} for all registered datatypes. % \begin{macrocode} \newcommand\multilang@regfield[2]{% \pgfqkeys{\multilang@keyof{#1}}{% #2/.code={\csdef{multilang@@val@#2}{##1}}}% \forlistloop{\multilang@regfieldtype{#1}{#2}}{\multilang@@types}} % \end{macrocode} % \end{macro} % % \begin{macro}{\multilang@regfieldtype} % The |\multilang@regfieldtype|\marg{cmd-or-env}\marg{fieldname}\marg{typename} % macro registers the \textsf{pgfkeys} key for \meta{typename} of % \meta{fieldname}. % \begin{macrocode} \newcommand\multilang@regfieldtype[3]{% \bgroup % \end{macrocode} % In the following, we check whether the number of arguments for the % \meta{typename} macro is 1, because for some reason |style n args| seems not % to work as we want it to work if $n=1$. % \begin{macrocode} \ifnumequal{\csuse{multilang@@typeargc@#3}}{1}{% \ifcsdef{multilang@@codetype@#3}{% \edef\do{\egroup\noexpand\pgfqkeys{\multilang@keyof{#1}}{% #2/#3/.code={\csexpandonce{multilang@@runcmd@#3}}% }}% }{% \edef\do{\egroup\noexpand\pgfqkeys{\multilang@keyof{#1}}{% #2/#3/.style={#2={\csexpandonce{multilang@@runcmd@#3}}}% }}% }% }{% \ifcsdef{multilang@@codetype@#3}{% \edef\do{\egroup\noexpand\pgfqkeys{\multilang@keyof{#1}}{% #2/#3/.code n args={\csuse{multilang@@typeargc@#3}}% {\csexpandonce{multilang@@runcmd@#3}}}}% }{% \edef\do{\egroup\noexpand\pgfqkeys{\multilang@keyof{#1}}{% #2/#3/.style n args={\csuse{multilang@@typeargc@#3}}% {#2={\csexpandonce{multilang@@runcmd@#3}}}}}% }% }\do} % \end{macrocode} % \end{macro} % % % \subsubsection{Argument Aliases} % % \begin{macro}{\multilang@regcomb} % The |\multilang@regcomb|\marg{cmd-or-env}\marg{alias}\marg{fields} % macro registers an \meta{alias} argument for \meta{fields}. % \begin{macrocode} \newcommand\multilang@regcomb[3]{% \multilang@regcombtype{#1}{#2}{#3}{}% \forlistloop{\multilang@regcomb@i{#1}{#2}{#3}}{\multilang@@types}} % \end{macrocode} % \end{macro} % % \begin{macro}{\multilang@regcomb@i} % The % |\multilang@regcomb@i|\marg{cmd-or-env}\marg{alias}\marg{fields}\marg{type} % macro is an auxiliary front-end to |\multilang@regcombtype| that transforms % \meta{type} to a key \meta{suffix} (by prepending a ``|/|''). % \begin{macrocode} \newcommand\multilang@regcomb@i[4]{% \multilang@regcombtype{#1}{#2}{#3}{/#4}} % \end{macrocode} % \end{macro} % % \begin{macro}{\multilang@regcombtype} % The % |\multilang@regcombtype|\marg{cmd-or-env}\marg{alias}\marg{fields}\marg{suffix} % registers the \meta{alias} with the given type-\meta{suffix}. % \begin{macrocode} \newcommand\multilang@regcombtype[4]{% \bgroup % \end{macrocode} % We count the number of field names in \meta{fields} (in |\@tempcnta|) and, % in the same loop, gather the individual field assignments (in |\toks@|). % \begin{macrocode} \toks@{}\@tempcnta=0\relax \forcsvlist{% \advance\@tempcnta by1\relax \expandafter\multilang@regcomb@set\expandafter{\the\@tempcnta}{#4}% }{#3}% % \end{macrocode} % Finally, we set the style for the \meta{alias}\meta{suffix} key. Again we % separately handle the case of a single field. Additionally, we treat also % the case of \emph{no} field special: It gets a |style| (with 1 argument), % but the argument is essentially ignored (as |\toks@| is empty). This makes % \meta{alias} a ``comment'' field that is not passed to the |command| or % |environment|. % \begin{macrocode} \ifnumgreater{\the\@tempcnta}{1}{% \edef\do{\egroup\noexpand\pgfqkeys{\multilang@keyof{#1}}{% #2#4/.style n args={\the\@tempcnta}{\the\toks@}}}% }{% \edef\do{\egroup\noexpand\pgfqkeys{\multilang@keyof{#1}}{% #2#4/.style={\the\toks@}}}% }% \do} % \end{macrocode} % \end{macro} % % \begin{macro}{\multilang@regcomb@set} % The |\multilang@regcomb@set|\marg{index}\marg{suffix}\marg{field} macro % appends the sequence ``\meta{field}\meta{suffix}|={#|\meta{index}|}|'' to % the |toks@| register. When used in a |.style n args| key, this sets the % ``\meta{field}\meta{suffix}'' key to the \meta{index}-th positional % parameter. % \begin{macrocode} \newcommand\multilang@regcomb@set[3]{% \toks@\expandafter{\the\toks@,#3#2={###1}}} % \end{macrocode} % \end{macro} % % % \subsubsection{Language ``Types''} % % \begin{macro}{\multilang@addlanguage} % The |\multilang@addlanguage|\marg{language} registers \meta{language}, % essentially registering ``\meta{language}'' and ``\meta{language}|!|'' as % argument datatypes. % \begin{macrocode} \newcommand\multilang@addlanguage[1]{% % \end{macrocode} % The following checks the current language (|\languagename|) against % \meta{language}. In the following, % |##1| is the argument to the key when the key is used. % \begin{macrocode} \ifdefstring{\languagename}{#1}% {\NewMultilangType{#1}{##1}}% {\NewMultilangType@code{#1}{}}% % \end{macrocode} % The following defines the ``\meta{language}|!|'' key for forcing an % argument to be formatted in language \meta{language}. % \begin{macrocode} \NewMultilangType{#1!}{\foreignlanguage{#1}{##1}}} % \end{macrocode} % \end{macro} % Register all languages passed as argument to the package. % \begin{macrocode} \forlistloop{\multilang@addlanguage}{\multilang@@langs} % \end{macrocode} % % % \subsection{Auxiliary Macros} % % \begin{macro}{\multilang@keyof} % The |\multilang@keyof|\marg{cmd-or-env}, when fully expanded such as in the % first argument of the |\pgfqkeys| macro, represents the key under which the % parameter keys of the command or environment \meta{cmd-or-env} are stored. % Note that the branching in the code below checks whether \meta{cmd-or-env} % is a command sequence (|true| case) or not (|false| case). % \begin{macrocode} \newcommand\multilang@keyof[1]{% \ifcat\relax\noexpand#1% /multilang/cmd/\expandafter\@gobble\string#1% \else /multilang/env/#1% \fi} % \end{macrocode} % \end{macro} % % \begin{macro}{\multilang@error} % The |\multilang@error|\marg{message} macro shows \meta{message} as an error % message of the \ThisPackage package. % \begin{macrocode} \newcommand\multilang@error[1]{\PackageError{multilang}{#1}{}} % \end{macrocode} % \end{macro} % % \begin{macro}{\multilang@processargs} % The |\multilang@processargs|\marg{cmd-or-env}\marg{opt-key}\marg{options} % macro processes the \meta{options} in key \meta{opt-key}. Afterwards, it % post-processes the arguments |margs|, |oargs|, |alias/...|, and % |disablable|. % It stores the result of the post-processing in the macros % |\multilang@@actuals|, |\multilang@@checks|, and |\multilang@@keys|. % \begin{macrocode} \newcommand\multilang@processargs[3]{% \let\multilang@@aliases=\empty \pgfqkeys{#2}{#3}% % \end{macrocode} % In the following, we iteratively construct three macros: % |\multilang@@actuals|, |\multilang@@checks|, and |\multilang@@keys|. % \begin{itemize}[nosep] % \item In |\multilang@@actuals|, we step by step construct the arguments that % shall be passed to \meta{cmd-or-env}. % \item In |\multilang@@checks|, we construct a list of preliminary checks % that \meta{cmd-or-env} shall perform on its arguments. % \item In |\multilang@@keys|, we construct a list of |\pgfqkeys| commands % that set up the keys for \meta{cmd-or-env}'s one argument. % \end{itemize} % The following first initializes the three macros. % \begin{macrocode} \edef\multilang@@actuals{}% \def\multilang@@checks{}% \def\multilang@@keys{}% % \end{macrocode} % We first process the optional arguments. We process them before the % mandatory arguments because they must come first in |\multilang@@actuals|. % In the following |\do| macro, |##1| iterates over all optional argument % names in the |margs| list. % \begin{macrocode} \ifdefvoid{\multilang@@oargs}{}{% \def\do##1{% \appto{\multilang@@actuals}{% \ifcsmacro{multilang@@val@##1}% {[\csexpandonce{multilang@@val@##1}]}% {}% }% \appto{\multilang@@keys}{\multilang@regfield{#1}{##1}}% }% \expandafter\docsvlist\expandafter{\multilang@@oargs}}% % \end{macrocode} % Next, we append the mandatory arguments, specified by the |margs| list. % In the following |\do| macro, |##1| iterates over all mandatory argument % names in the |margs| list. % \begin{macrocode} \ifdefvoid{\multilang@@margs}{}{% \def\do##1{% \appto{\multilang@@actuals}{% {\csexpandonce{multilang@@val@##1}}% }% \appto{\multilang@@checks}{% \ifcsmacro{multilang@@val@##1}% {}% {\multilang@error{mandatory argument ##1 missing}}% }% \appto{\multilang@@keys}{\multilang@regfield{#1}{##1}}% }% \expandafter\docsvlist\expandafter{\multilang@@margs}}% % \end{macrocode} % Afterwards, we handle argument aliases. The list of aliases' names is in % |\multilang@@aliases| and the list of arguments that a \meta{alias} % combines is in |\multilang@@alias@|\meta{alias}. % Note that aliases only modify |\multilang@@keys| -- i.e., not % |\multilang@@actuals| or |\multilang@@checks|. % \begin{macrocode} \def\do##1{% \eappto{\multilang@@keys}{% \unexpanded{\multilang@regcomb{#1}{##1}}% {\csuse{multilang@@alias@##1}}}}% \expandafter\dolistloop\expandafter{\multilang@@aliases}% % \end{macrocode} % To handle |disablable| macros, we simply add the |disabled| key. This key is % a Boolean key that just sets a conditional. % \begin{macrocode} \ifbool{multilang@@disablable}% {\eappto{\multilang@@keys}{% \noexpand\pgfqkeys{\multilang@keyof{#1}}{% disabled/.is if={multilang@cmd@@disabled}}}}% {}% % \end{macrocode} % Invoke the hook. To avoid macro arguments to the hook, we store the command or % environment name in |\multilang@@cmdorenv| and store the \textsf{pgfkeys} key % for the command or environment in |\multilang@@cekey|. % \begin{macrocode} \def\multilang@@cmdorenv{#1}% \edef\multilang@@cekey{\multilang@keyof{#1}}% \multilang@hook@processargs } % \end{macrocode} % \end{macro} % % \begin{macro}{\multilang@hook@processargs} % The |\multilang@hook@processargs| macro is a hook that enables extensions to % the |\multilang@processargs|. % \begin{macrocode} \newcommand\multilang@hook@processargs{} % \end{macrocode} % \end{macro} % % \begin{macro}{\multilang@noend} % The |\multilang@noend|\marg{body} macro is intended to be used as the % argument to |\Collect@Body| (or |\collect@body|) of the \textsf{environ} % package. In this context, it performs the following: % Firstly, it ignores the collected \meta{body} of the environment. % Secondly, it temporarily disables the |\end|-code of the environment in % which the |\Collect@Body| is expanded. % \begin{macrocode} \newcommand\multilang@noend[1]{\cslet{end\@currenvir}{\relax}} % \end{macrocode} % \end{macro} % %\iffalse % %<*pkgtags> %\fi % % \section{Implementation of Tags} % % \begin{macro}{\SetTagFilter} % The |\SetTagFilter|\oarg{default}\marg{policy} sets the tag filter policy % based on accept/deny rules in \meta{policy}. % \begin{macrocode} \newcommand\SetTagFilter[2][accept]{% \kcvml@parsepolicy{kcvml@@tagfilter}{#1}{#2}} \newcommand\kcvml@@tagfilter{} % \end{macrocode} % \end{macro} % % \begin{macro}{\DefineTagFilter} % The |\DefineTagFilter|\marg{name}\marg{default}\marg{policy} macro defines a % tag filter policy under name \meta{name}. % \begin{macrocode} \newcommand\DefineTagFilter[3]{% \kcvml@parsepolicy{kcvml@filter@@#1}{#2}{#3}} % \end{macrocode} % \end{macro} % % \begin{macro}{\UseFilter} % The |\UseTagFilter|\marg{name} macro uses the previously defined tag filter % policy with name \meta{name}. % \begin{macrocode} \newcommand\UseTagFilter[1]{% \letcs\kcvml@@tagfilter{kcvml@filter@@#1}} % \end{macrocode} % \end{macro} % % \begin{macro}{\kcvml@parsepolicy} % The |\kcvml@parsepolicy|\marg{csname}\marg{default}\marg{policy} % parses a tag filter policy, \meta{policy} with default \meta{default}, and % stores the resulting filter in the control sequence \meta{csname}. % \begin{macrocode} \newcommand\kcvml@parsepolicy[3]{% % \end{macrocode} % We first reset the temporary filter variable |\kcvml@@tmptagfilter| then % populate it via |\pgfqkeys|, first with the \meta{policy} list and % subsequently with the \meta{default} policy. % \begin{macrocode} \bgroup \def\kcvml@@tmptagfilter{}% \pgfqkeys{kcvml/tagfilter}{#3,default/#2}% % \end{macrocode} % Now we export the temporary |\kcvml@@tmptagfilter| to outside the local group % and into the control sequence \meta{csname}. % \begin{macrocode} \edef\do{\egroup \unexpanded{\csdef{#1}}{\expandonce{\kcvml@@tmptagfilter}}}% \do} % \end{macrocode} % The following lines specify how |\kcvml@@tmptagfilter| is modified when % |accept| or |deny| filter rules are specified. % \begin{macrocode} \pgfqkeys{kcvml/tagfilter}{% accept/.code={\kcvml@appendrule{#1}{\boolfalse}{\booltrue}}, deny/.code ={\kcvml@appendrule{#1}{\booltrue}{\boolfalse}}, default/accept/.code n args={0}{\appto\kcvml@@tmptagfilter{% \kcvml@applydefault{\boolfalse}}}, default/deny/.code n args={0}{\appto\kcvml@@tmptagfilter{% \kcvml@applydefault{\booltrue}}}, } % \end{macrocode} % \end{macro} % % \begin{macro}{\kcvml@appendrule} % The |\kcvml@appendrule|\marg{ruletags}\marg{flagmacro}\marg{invmacro} macro % appends a filter rule to the overall tag filter. The rule filters for the % given comma-separated list \meta{ruletags}. % The \meta{flagmacro} specifies whether a match shall be disabled (if % |\booltrue|) or enabled (if |\boolfalse|). % The \meta{invmacro} must be the inverse of \meta{flagmacro}. % \begin{macrocode} \newcommand\kcvml@appendrule[3]{% \bgroup % \end{macrocode} % For simplified later processing, we turn \meta{ruletags} into an % \textsf{etoolbox} list (in |\kcvml@@ruletags|) first. % \begin{macrocode} \def\kcvml@@ruletags{}% \forcsvlist{\listadd{\kcvml@@ruletags}}{#1}% % \end{macrocode} % Now we append to the overall filter (in |\kcvml@@tmptagfilter|) outside the % local group and use the cascade of |\expandafter|s to get |\cvmkl@@ruletags| % out of the group without polluting the outer scope. % \begin{macrocode} \expandafter\egroup \expandafter\listadd\expandafter\kcvml@@tmptagfilter\expandafter{% \expandafter\kcvml@applyrule\expandafter{\kcvml@@ruletags}{#2}{#3}}} % \end{macrocode} % \end{macro} % % We add the additional |tags| key to every disablable multilingual macro and % environment. For this, we use \textsf{multilang}'s % |\multilang@hook@processargs| hook. % \begin{macrocode} \appto\multilang@hook@processargs{% \ifbool{multilang@@disablable}% {\eappto{\multilang@@keys}{% % \end{macrocode} % Note that in |\multilang@@cekey|, the parent key of the command or environment % is stored. Whenever the |tags| argument is used, we make it invoke % |\kcvml@applyfilter| with the given \meta{tags} (|##1|). % \begin{macrocode} \noexpand\pgfqkeys{\multilang@@cekey}{% tags/.code={\noexpand\kcvml@applyfilter{##1}}}}} {}} % \end{macrocode} % % \begin{macro}{\kcvml@applyfilter} % The |\kcvml@applyfilter|\marg{tags} macro applies the current filter, which is % in the |\kcvml@@tagfilter| \textsf{etoolbox} list, to the comma-separated list % \meta{tags} of tags to check whether the entity with the \meta{tags} should be % disabled or not. The result of the check is stored in the Boolean flag % |multilang@cmd@@disabled| for further use in with \textsf{multilang} code. % \begin{macrocode} \newcommand\kcvml@applyfilter[1]{% \ifbool{multilang@cmd@@disabled}{}{% % \end{macrocode} % We check the filter only if the entry has not already explicitly been marked % as disabled. I.e., explicit disabling takes precedence, no matter whether it % is specified before or after a |tags| element. % In the Boolean flag |kcvml@@match|, we store whether a tag in \meta{tags} % matched one of the accept/deny filters already. After initializing this flag, % we iterate through |\kcvml@@tagfilter| with |\do|\marg{rule}, and % we stop iterating after a match has been found. % \begin{macrocode} \boolfalse{kcvml@@match}% \def\do##1{% % \end{macrocode} % Note that \meta{rule} is a curried macro whose missing last argument, % \meta{tags}, is added here. % \begin{macrocode} ##1{#1}% \ifbool{kcvml@@match}{\listbreak}{}}% \dolistloop{\kcvml@@tagfilter}}} \newbool{kcvml@@match} % \end{macrocode} % \end{macro} % % \begin{macro}{\kcvml@applyrule} % The % |\kcvml@applyrule|\marg{ruletags}\marg{flagmacro}\marg{invmacro}\marg{tags} % macro applies a single filter rule to the given \meta{tags}. % The \meta{flagmacro} must be either |\booltrue| or |\boolfalse|. % If it is |\booltrue|, this specifies that a match of \meta{tags} against % \meta{ruletags} disables the display of the respective entity; % If it is |\boolfalse|, this specifies that a match enables the display. % The \meta{invmacro} must be the inverse of \meta{flagmacro}. % We just check each tag in \meta{tags} individually. % \begin{macrocode} \newcommand\kcvml@applyrule[4]{% \forcsvlist{\kcvml@applyrule@i{#1}{#2}{#3}}{#4}} % \end{macrocode} % % \begin{macro}{\kcvml@applyrule@i} % The % |\kcvml@applyrule@i|\marg{ruletags}\marg{flagmacro}\marg{invmacro}\marg{tag} % macro applies a single filter rule to the given \meta{tag}. % It checks whether the \meta{tag} is inverted (i.e., starting with ``|!|'') and % hands over the actual check to |\kcvml@applyrule@ii|. % \begin{macrocode} \newcommand\kcvml@applyrule@i[4]{% \if !\@car#4\@nil \expandafter\kcvml@applyrule@ii\expandafter{\@cdr#4\@nil}{#1}{#3}% \else \kcvml@applyrule@ii{#4}{#1}{#2}\fi} % \end{macrocode} % \begin{macro}{\kcvml@applyrule@ii} % The % |\kcvml@applyrule@ii|\marg{tag}\marg{ruletags}\marg{flagmacro} % macro applies a single filter rule to the given \meta{tag}. % We check whether \meta{tag} in in the \textsf{etoolbox} list \meta{ruletags}. % Since |\ifinlist| expects a list macro for its second argument (which it then % expands once), we just give it |\empty| to eat (expand) and then use % \meta{ruletags} as it is. % \begin{macrocode} \newcommand\kcvml@applyrule@ii[3]{% \ifinlist{#1}{\empty #2}% % \end{macrocode} % If we have a match, we apply \meta{flagmacro} to |multilang@cmd@@disabled| and % then record, in |kcvml@@match|, that we found a match. % \begin{macrocode} {#3{multilang@cmd@@disabled}\booltrue{kcvml@@match}}{}} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\kcvml@applydefault} % The |\kcvml@applydefault|\marg{flagmacro}\marg{tags} macro applies a default % filter to \meta{tags}. A \meta{flagmacro} of |\booltrue| corresponds to a % ``default deny''; |\boolfalse| corresponds to ``default accept''. % \begin{macrocode} \newcommand\kcvml@applydefault[2]{% #1{multilang@cmd@@disabled}} % \end{macrocode} % \end{macro} % %\iffalse % %<*pkgsect> %\fi % % \section{Implementation of Sectioning Environments} % % The sectioning environments are proxies for the corresponding sectioning % macros. They are defined as environments such that the whole environments % rather than just the headings can be disabled. % Each of the environments has one optional argument, |short| (for a short % title), and one mandatory argument, |title| (for the actual title). % % \begin{environment}{Section} % \begin{environment}{Section*} % The |Section| and |Section*| environments are % multilingual proxies to |\section| and, respectively, |\section*|. % \begin{macrocode} \NewMultilangEnv{Section}{disablable, environment=section, oargs=short, margs=title} \NewMultilangEnv{Section*}{disablable, environment=multilang@secstar, oargs=short, margs=title} \newenvironment{multilang@secstar}{\section*}{} % \end{macrocode} % \end{environment} % \end{environment} % % \begin{environment}{SubSection} % \begin{environment}{SubSection*} % The |SubSection| and |SubSection*| environments are % multilingual proxies to |\subsection| and, respectively, |\subsection*|. % \begin{macrocode} \NewMultilangEnv{SubSection}{disablable, environment=subsection, oargs=short, margs=title} \NewMultilangEnv{SubSection*}{disablable, environment=multilang@ssecstar, oargs=short, margs=title} \newenvironment{multilang@ssecstar}{\subsection*}{} % \end{macrocode} % \end{environment} % \end{environment} % % \begin{environment}{SubSubSection} % \begin{environment}{SubSubSection*} % The |SubSubSection| and |SubSubSection*| environments are % multilingual proxies to |\subsubsection| and, respectively, |\subsubsection*|. % \begin{macrocode} \NewMultilangEnv{SubSubSection}{disablable, environment=subsubsection, oargs=short, margs=title} \NewMultilangEnv{SubSubSection*}{disablable, environment=multilang@sssecstar, oargs=short, margs=title} \newenvironment{multilang@sssecstar}{\subsubsection*}{} % \end{macrocode} % \end{environment} % \end{environment} % % \begin{environment}{Paragraph} % \begin{environment}{Paragraph*} % The |Paragraph| and |Paragraph*| environments are % multilingual proxies to |\paragraph| and, respectively, |\paragraph*|. % \begin{macrocode} \NewMultilangEnv{Paragraph}{disablable, environment=paragraph, oargs=short, margs=title} \NewMultilangEnv{Paragraph*}{disablable, environment=multilang@parstar, oargs=short, margs=title} \newenvironment{multilang@parstar}{\paragraph*}{} % \end{macrocode} % \end{environment} % \end{environment} % % \begin{environment}{SubParagraph} % \begin{environment}{SubParagraph*} % The |SubParagraph| and |SubParagraph*| environments are % multilingual proxies to |\subparagraph| and, respectively, |\subparagraph*|. % \begin{macrocode} \NewMultilangEnv{SubParagraph}{disablable, environment=subparagraph, oargs=short, margs=title} \NewMultilangEnv{SubParagraph*}{disablable, environment=multilang@sparstar, oargs=short, margs=title} \newenvironment{multilang@sparstar}{\subparagraph*}{} % \end{macrocode} % \end{environment} % \end{environment} % % %\iffalse % %\fi % \Finale \endinput