# String variable with multiple values

I'm after a definition of a command that could have multiple values to use in different contexts. The use case for this would be in a custom class that uses two languages simultaneously, or I guess any situation where a variable could have different values depending on the environment. After searching information on \newcommand and things related to it, I'm still a bit puzzled about things.

As I understand it, a simple implementation would be:

\newcommand\varname{text value}


However, having different commands for same things gets ugly in my eye. For example, I have in front of me an old class with commands like Doctype and Doctypefin for the Finnish version. Instead, I'd like to use the command like this:

% Definitions
\title{en}{Title Of The Document}
\title{fi}{Dokumentin otsikko}

% Use in class environments or tex files
\title{en} % -> "Title Of The Document"
\title{fi} % -> "Dokumentin otsikko"


Is there any way to construct such a macro or definition?

Ideally, specifying unseen categories (here languages) would not be a problem, but I can see it would be reasonable to require specifying the accepted values in the class file. Also, like in the example below, I think it would be acceptable to have a separate command for outputting the variable, if that's a problem.

I've tried to take note of the way other class commands are constructed. There's a handy MakeStringVar command that constructs a variable, and if not set, displays a default text. That default text functionality would be very useful for the new command, because loads of the environments in the class use the definitions to output text to title pages etc.

\newcommand\MakeStringVar[2][\relax]{%
\ifx#1\relax%
\expandafter\newcommand\csname Emit#2\endcsname{%
{\scriptsize (Use {\tt\textbackslash #2} to replace this text.)}}%
\else%
\expandafter\newcommand\csname Emit#2\endcsname{#1}%
\fi%
\expandafter\newcommand\csname #2\endcsname[1]{%
\expandafter\renewcommand\csname Emit#2\endcsname{##1}%
}%
}


It is used like so:

\MakeStringVar{Major} % Definition in class
\Major{Major subject name} % Set value in pre-document
\EmitMajor % Used in environments in class -> "Major subject name"


But I'm a newbie when it comes to LaTeX, so I'm not sure where to even begin. I have a feeling the above command could be expanded in some manner, but I really don't know the limitations of LaTeX macros.

• Have you seen this question? It addresses a method for parsing a simple string for matches.
– WesH
Feb 27, 2019 at 17:56
• @WesH Thanks, for the link. I'll investigate. Seems to apply quite well. Feb 27, 2019 at 18:02
• So you're comfortable with using different command for the different contexts? As in, \title[en]{<English title>} and \title[fi]{<Finnish title>} (say) together with \thetitle[en] (for <English title>) and/or \thetitle[fi] (for <Finnish title>). Note the use of \title for defining the title and \thetitle to set the title in the document. Also, the language choice is presented as an optional argument, where you can specify some default (like en for English, say).
– Werner
Feb 27, 2019 at 18:15
• @Werner I am aware, that that's basically what's already happening :D but yes, I would. Even to have the argument. Call me crazy. And yeah, a default argument for the language would be a good addition! Feb 27, 2019 at 18:18

You may consider this approach which uses \@namedef:

\documentclass{article}

\makeatletter
\newcommand\deftitle[2][en]{%
\global\@namedef{title:#1}{#2}%
}
\newcommand\usetitle[1][en]{\@nameuse{title:#1}}
\makeatother

\begin{document}

\deftitle{Default Language (English) Title} % same as \deftitle[en]{...}
\deftitle[it]{Italian Title}
\deftitle[fr]{French Title}

\usetitle[it]
\usetitle % same as \usetitle[en]
\usetitle[fr]

\end{document}


When the user calls \deftitle[en]{<content>}, a new macro title:en is defined and it expands to <content> when called via \@nameuse.

EDIT: Here there's a general way to construct such macros:

\makeatletter
\newcommand\newconstructor[1]{%
\expandafter\newcommand\csname def#1\endcsname[2][en]{%
\global\@namedef{#1:##1}{##2}%
}%
\expandafter\newcommand\csname use#1\endcsname[1][en]{\@nameuse{#1:##1}}%
}
\makeatother


Now, for example, \newconstructor{title} defines \deftitle and \usetitle, like before.

• Is there an equally simple way of defining the macro "factory" so to speak, as in the example in my question? Feb 27, 2019 at 19:53
• @Felix I've added a general constructor in my answer. Feb 27, 2019 at 20:24
• Man. You've made my next few months a lot easier <3 Feb 27, 2019 at 20:29

In this implementation the various versions are input with a handy key-value interface (brace the value if it is to contain a comma).

Also aliases for keys can be defined. For languages, I think it's preferable to use long keys with the full language name, so \languagename can be used to get the related string. However, aliases for keys can also be used in the document, provided they're defined beforehand.

You're not compelled to add all versions at the time of a variable definition as you can use \addtovarstring later.

\documentclass{article}
\usepackage[english,finnish]{babel}

\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand{\definevarstring}{mO{}}
{
\prop_new:c { g_felix_varstring_#1_prop }
\felix_varstring_add:nn { #1 } { #2 }
}
{
\felix_varstring_add:nn { #1 } { #2 }
}
\NewDocumentCommand{\definealias}{m}
{
\prop_gset_from_keyval:Nn \g_felix_varstring_alias_prop { #1 }
}
\NewExpandableDocumentCommand{\getvarstring}{mm}
{
\prop_if_in:cfTF { g_felix_varstring_#1_prop } { #2 }
{
\prop_item:cf { g_felix_varstring_#1_prop } { #2 }
}
{
\prop_if_in:NnT \g_felix_varstring_alias_prop { #2 }
{
\prop_item:cf { g_felix_varstring_#1_prop }
{
\prop_item:Nn \g_felix_varstring_alias_prop { #2 }
}
}
}
}

\cs_generate_variant:Nn \prop_item:Nn { cf }
\prg_generate_conditional_variant:Nnn \prop_if_in:Nn { cf } { T,F,TF,p }
\prop_new:N \g_felix_varstring_alias_prop

{
\prop_gset_from_keyval:cn { g_felix_varstring_#1_prop } { #2 }
}

\ExplSyntaxOff

\definealias{fi=finnish,en=english}

\definevarstring{title}[% long versions for languages
english=Title of the document,
finnish=Dokumentin otsikko,
]

\begin{document}

\author{A. Uthor}
\title{\getvarstring{title}{\languagename}}
\maketitle

\selectlanguage{english}

\getvarstring{title}{\languagename}

\getvarstring{title}{en}---\getvarstring{title}{english}

\getvarstring{title}{fi}---\getvarstring{title}{finnish}

\end{document}


Some time ago I wrote a \SetProperty-\GetProperty-interface.

Probably it is of use to you.

%% This coding example was written by Ulrich Diez in November 16, 2018.
%% It was modified by Ulrich Diez in February 28, 2019.
%%
%% Copyright 2018, 2019 Ulrich Diez (e-mail: ud.usenetcorrespondence@web.de)
%%
%% This work may be distributed and/or modified under the conditions of
%% the LaTeX Project Public License, either version 1.3c of this license
%% or (at your option) any later version.
%%
%%
%%   <http://www.latex-project.org/lppl.txt>
%%
%% and version 1.3x or later is part of all distributions of LaTeX
%% version 2005/12/01 or later.
%%
%% This work has the LPPL maintenance status unmaintained'.
%%
%% This work consists of this coding example.
%%
\errorcontextlines=10000
\documentclass[landscape]{article}

\makeatletter
%%==== Begin of code for the \SetProperty-\GetProperty-Interface =======
\RequirePackage{ifluatex, ifxetex}
%%======================================================================
%% Paraphernalia:
%%    \UD@firstoftwo, \UD@secondoftwo, \UD@PassFirstToSecond,
%%    \UD@exchange, \UD@removespace, \UD@name, \UD@CheckWhetherNull,
%%    \UD@loopcall,
%%......................................................................
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@PassFirstToSecond[2]{#2{#1}}%
\newcommand\UD@exchange[2]{#2#1}%
\newcommand\UD@removespace{}\UD@firstoftwo{\def\UD@removespace}{} {}%
%%----------------------------------------------------------------------
%% Put a control sequence token in place instead of the string denoting
%% its name.
%%......................................................................
%%  \UD@name<emptiness or tokens other than braces>{<Name of
%%                                                   Control Sequence>}
%%
%% yields:
%%
%%  <emptiness or tokens other than braces>\Controlsequence
%%
%% E.g.,
%%
%%   \UD@name foo{bar} -> foo\bar
%%   \UD@name{bar} -> \bar
%%   \UD@name\newcommand*{wEirdName}[1]{Arg 1: (#1)}
%%       -> \newcommand*\wEirdName[1]{Arg 1: (#1)}
%%
\newcommand\UD@name{}\long\def\UD@name#1#{\romannumeral\UD@@name{#1}}%
\newcommand\UD@@name[2]{%
\expandafter\UD@exchange\expandafter{\csname#2\endcsname}{0 #1}%
}%
%%----------------------------------------------------------------------
%% Check whether argument is empty:
%%......................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is empty>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%%
%% (\romannumeral expansion was introduced in order to overcome the
%%  concerns and worries about improperly balanced
%%  \if..\else..\fi constructs.)
%%
\newcommand\UD@CheckWhetherNull[1]{%
\romannumeral0\expandafter\UD@secondoftwo\string{\expandafter
\UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
\UD@secondoftwo\string}\expandafter\expandafter\UD@firstoftwo{ }{}%
\UD@secondoftwo}{\expandafter\expandafter\UD@firstoftwo{ }{}\UD@firstoftwo}%
}%
%%----------------------------------------------------------------------
%% Expandable Loop:
%% \UD@loopcall{<action>}%
%%          {<action if list empty>}%
%%          {<preset>}%
%%          {{<e_k>}{<e_(k+1)>}..{e_n}}% <- this is the list
%%
%% If list is empty: <action if list empty>
%% Else:
%% <action>{<e_k>}<preset> \UD@loopcall{<action>}%
%%                                  {<action if list empty>}%
%%                                  {<preset>}{{<e_(k+1)>}..{e_n}}
%%
%% <action> can be defined to mesh into the iteration-process, e.g.,
%% (ex)changing arguments like the <action if list empty>-argument for
%% the next \UD@loopcall-iteration, e.g., terminating iteration
%% prematurely under some circumstances.
%%......................................................................
\newcommand\UD@RemoveTillUD@nil{}%
\long\def\UD@RemoveTillUD@nil#1#2\UD@nil{{#1}}%
\newcommand\UD@Extractfirstloop[1]{%
\expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
{\UD@exchange{#1}}%
{%
\expandafter\UD@Extractfirstloop
\expandafter{\UD@RemoveTillUD@nil#1}%
}%
}%
\newcommand\UD@loopcall[4]{%
\UD@CheckWhetherNull{#4}{#2}{%
\expandafter\UD@exchange
\expandafter{\expandafter{\UD@firstoftwo{}#4}}%
{\UD@Extractfirstloop{#4\UD@nil}{#1}#3\UD@loopcall{#1}{#2}{#3}}%
}%
}%
%%======================================================================
%% Expandable comparison of two strings:
%%
%% Derived from David Kastrup's \ifstrequal-test from the
%% TeX Pearl Diving Site;
%% Pearls of 2005;
%% Title: David Kastrup - Comparing two strings known to consist
%% only of characters ;
%% Url: <http://www.gust.org.pl/projects/pearls/2005p/david-kastrup/bachotex2005-david-kastrup-pearl1.pdf>
%%......................................................................
%% \UD@ifstrequal{<String 1>}{<String 2>}%
%%               {%
%%                  <Tokens to be delivered in case strings are equal>
%%               }%
%%               {%
%%                  <Tokens to be delivered in case strings are
%%                   not equal>
%%               }%
%%
%% (<String 1> gets expanded during comparison.
%%  <String 2> gets not expanded during comparison.
%%
%% I bloated this thing up for ensuring it also takes spaces into
%% account.
%%
\@ifdefinable\UD@strchksp{%
\long\def\UD@strchksp#1#2#3#4 #5\relax{%
#1{\if\UD@strequal\UD@secondoftwo{}{#4}%
\expandafter\UD@firstoftwo\else\expandafter\UD@secondoftwo\fi
{\if\UD@strequal\UD@secondoftwo{}{#5}%
\expandafter\UD@firstoftwo\else\expandafter\UD@secondoftwo\fi
{\UD@strequalstart#1{#2}{#3} \relax}%
{\UD@strequalstart#1{#2}{#3}{ }#5\relax}}}%
{\UD@strequalstart#1{#2}{#3}#4 #5\relax}%
}%
}
\newcommand\UD@strequal[2]{\number\UD@strchksp#1{}{}#2 \relax}
\newcommand\UD@strequalstart[4]{%
\if#4\relax\UD@strequalstop\fi
\UD@strchksp#1{\if#4#2}{#3\fi}%
}%
\@ifdefinable\UD@strequalstop{%
\long\def\UD@strequalstop\fi\UD@strchksp#1#2#3#4{\fi#2#4\relax'#313 }%
}
\newcommand\UD@ifstrequal[2]{%
\romannumeral0%
\if\UD@strequal\@firstofone{#2.}{#1.}%
\expandafter\UD@firstoftwo\else\expandafter\UD@secondoftwo\fi
{\UD@exchange{ \UD@firstoftwo}}{\UD@exchange{ \UD@secondoftwo}}%
{\expandafter\expandafter\expandafter}%
}%
%%======================================================================
%% Total expansion and stringification of argument:
%%......................................................................
%% \UD@stringify{<argument that expands to character tokens>}
%%
%% Does \csname..\endcsname with the argument, then \string,
%% then removal of \escapechar if that was added.
%%
\newcommand\UD@stringify[1]{%
\romannumeral0%
\expandafter\expandafter\expandafter\UD@exchange
\expandafter\expandafter\expandafter{%
\expandafter\string\csname#1\endcsname}%
{%
\ifnum\the\escapechar<0 %
\expandafter\UD@secondoftwo\else\expandafter\UD@firstoftwo\fi
{%
\ifnum\the\escapechar>\ifxetex 1114111 %
\else\ifluatex 1114111 \else 255 \fi\fi
\expandafter\UD@secondoftwo\else\expandafter\UD@firstoftwo\fi
{%
\ifnum\the\escapechar=32 %
\expandafter\UD@secondoftwo\else\expandafter\UD@firstoftwo\fi
{\UD@firstoftwo{ }}{}%
}{ }%
}{ }%
}%
}%
%%======================================================================
%% Property-Management:
%%......................................................................
%%
%% The concept is about maintaining macros that expand to lists of
%% 2-tuple-arguments.
%% The first component of the tuple holds the name of a property.
%% The second component holds the value of the property.
%%
%% E.g., the macro \macro could be defined to expand to:
%%
%%   {{property name 1}{property value 1}}%
%%   {{property name 2}{property value 2}}%
%%   ...
%%   {{property name K}{property value K}}%
%%
%%
%% \SetProperty{\macro}%
%%             {<name of property>}%
%%             {<new value of property>}
%%
%%   In case \macro is undefined, \macro will be defined empty.
%%
%%   In case a property <name of property> does not exist within the
%%   macro \macro, it will be added to the macro and it will get the
%%   value <new value of property>.
%%
%%   In case a property <name of property> does exist within the macro
%%   \macro, its value will get replaced by <new value of property>.
%%
%%   Before further evaluation <name of property> will be expanded via
%%   \csname..\endcsname-expansion and afterwards "stringified" by
%%   applying \string and removing a leading escapechar if one was
%%
%%   Example:
%%
%%     \SetProperty{\macro}{property name 2}{changed property value 2}
%%
%%        would make \macro to expand to
%%
%%     {{property name 1}{property value 1}}%
%%     {{property name 2}{changed property value 2}}%
%%     ...
%%     {{property name K}{property value K}}%
%%
%%       and
%%
%%     \SetProperty{\macro}{property name (K+1)}{property value (K+1)}
%%
%%        would make \macro to expand to
%%
%%     {{property name 1}{property value 1}}%
%%     {{property name 2}{changed property value 2}}%
%%     ...
%%     {{property name K}{property value K}}%
%%     {{property name (K+1)}{property value (K+1)}}%
%%
%%
%% \GetProperty{\macro}%
%%             {<name of property>}%
%%             {%
%%               <tokens to be delivered in case
%%                property is not available>
%%             }
%%
%%   In case \macro is undefined or property <name of property> does not
%%   exist within the macro \macro,
%%   <tokens to be delivered in case property is not available>
%%   will be delivered.
%%
%%   In case a property <name of property> does exist within the macro
%%   \macro, its value will be delivered.
%%
%%   Before further evaluation <name of property> will be expanded via
%%   \csname..\endcsname-expansion and afterwards "stringified" by
%%   applying \string and removing a leading escapechar if one was
%%
%%   \GetProperty is expandable and delivers the result after two
%%   expansion steps / after being hit "twice" by \expandafter.
%%
%%   E.g., with the macro \macro being defined to expand to
%%
%%     {{property name 1}{property value 1}}%
%%     {{property name 2}{property value 2}}%
%%     ...
%%     {{property name K}{property value K}}%
%%
%%   , the sequence
%%
%%     \GetProperty{\macro}{property name 2}{Huh?}
%%
%%   will expand to:
%%
%%     property value 2
%%
\newcommand\UD@ExpandProperties[3]{%
\expandafter\UD@PassFirstToSecond\expandafter{#1}%
{\expandafter\expandafter\expandafter\UD@PassFirstToSecond
\expandafter\expandafter\expandafter{%
\UD@stringify{#2}}{#3}}%
}%
\newcommand\UD@AtIfPropertyListUndefined[1]{%
\@ifundefined{%
\expandafter\UD@exchange\expandafter{\string#1}%
{%
\ifnum\the\escapechar<0 %
\expandafter\UD@secondoftwo\else\expandafter\UD@firstoftwo\fi
{%
\ifnum\the\escapechar>\ifxetex 1114111 %
\else\ifluatex 1114111 \else 255 \fi\fi
\expandafter\UD@secondoftwo\else\expandafter\UD@firstoftwo\fi
{%
\ifnum\the\escapechar=32 %
\expandafter\UD@secondoftwo\else\expandafter\UD@firstoftwo\fi
{\UD@firstoftwo{}}{\UD@removespace}%
}{}%
}{}%
}%
}%
}%
\newcommand\SetProperty[2]{%
\@bsphack
\UD@AtIfPropertyListUndefined{#1}{\newcommand*#1{}}{}%
\romannumeral0%
\UD@ExpandProperties{#1}{#2}{\UD@@setproperty}{#1}%
}%
\newcommand\UD@@setproperty[4]{%
\UD@loopcall{\UD@@@setproperty}%
{ \global\def#3{{{#1}{#4}}}\@esphack}%
{{#1}{#4}{ \global\def#3}{}}%
{#2}%
}%
\newcommand\UD@@@setproperty{}%
\long\def\UD@@@setproperty#1#2#3#4#5\UD@loopcall#6#7#8#9{%
\UD@ifstrequal{\UD@firstoftwo#1}{#2}%
{%
#4{#5{{#2}{#3}}#9}\@esphack
}{%
\UD@loopcall{#6}%
{#4{#5{#1}{{#2}{#3}}}\@esphack}%
{{#2}{#3}{#4}{#5{#1}}}%
{#9}%
}%
}%
\newcommand\GetProperty[3]{%
\romannumeral0%
\UD@AtIfPropertyListUndefined{#1}{ #3}{%
\UD@ExpandProperties{#1}{#2}{\UD@@getproperty}{ #3}%
}%
}%
\newcommand\UD@@getproperty[3]{%
\UD@loopcall{\UD@@@getproperty}%
{#3}%
{{#1}}%
{#2}%
}%
\newcommand\UD@@@getproperty{}%
\long\def\UD@@@getproperty#1#2\UD@loopcall#3#4#5#6{%
\UD@ifstrequal{\UD@firstoftwo#1}{#2}%
{\UD@exchange{ }\expandafter\UD@secondoftwo#1}%
{%
\UD@loopcall{#3}%
{#4}%
{#5}%
{#6}%
}%
}%
%%==== End of code for the \SetProperty-\GetProperty-Interface =========

%%==== Layout of this example ==========================================
%   - No headers / no footers
\pagestyle{empty}
%   - paragraph-breaking:
\parindent=0ex
\parskip=\medskipamount
%   - horizontal margins:
\setlength\textwidth{\paperwidth}
\setlength\oddsidemargin{1.5cm}%
\setlength\evensidemargin{\oddsidemargin}%
\setlength\marginparwidth{1.5cm}%
%   - vertical margins:
\setlength\topmargin{1.5cm}
%     pagestyle=empty:
\setlength\footskip{0pt}
\setlength\textheight{\paperheight}
%   - allow linebreaks after closing braces in typewriter font:
\DeclareFontFamily{\encodingdefault}{\ttdefault}{\hyphenchar\font=\}}
%   - in case of pdftex also adjust the underlying paper
\@ifundefined{pdfpagewidth}{}{\pdfpagewidth=\paperwidth}%
\@ifundefined{pdfpageheight}{}{\pdfpageheight=\paperheight}%
\@ifundefined{pagewidth}{}{\pagewidth=\paperwidth}%
\@ifundefined{pageheight}{}{\pageheight=\paperheight}%
%%==== Layout-changes etc done.=========================================

\newcommand\Errortext[3]{%
\nfss@text{\reset@font\bfseries#3}%
\GenericError{\space\@spaces\@spaces}%
{Error: \string#1: Translation into language #2 not available}%
{Source for further information on this error is neither available nor needed.}%
{You can use \string\SetProperty for specifying a translation.}%
}%

\SetProperty{\mymacro}{English}{This is a sentence in English.}
\SetProperty{\mymacro}{Francais}{Ceci est une phrase en fran\c cais.}
\SetProperty{\mymacro}{Italiano}{Questa \e una frase in italiano.}
\SetProperty{\mymacro}{Deutsch}{Dies ist ein Satz in deutscher Sprache.}

\makeatother

\begin{document}
\verb|\SetProperty{\mymacro}{English}{This is a sentence in English.}|\\
\verb|\SetProperty{\mymacro}{Francais}{Ceci est une phrase en fran\c cais.}|\\
\verb|\SetProperty{\mymacro}{Italiano}{Questa \e una frase in italiano.}|\\
\verb|\SetProperty{\mymacro}{Deutsch}{Dies ist ein Satz in deutscher Sprache.}|
\\\null\hrulefill\null

\verb|\GetProperty{\mymacro}{English}{\Errortext{\mymacro}{English}{No translation available.}}|\\
\GetProperty{\mymacro}{English}{\Errortext{\mymacro}{English}{No translation available.}}