Problem with command expansion

\documentclass{article}
\usepackage[utf8]{inputenc}
\usepackage{etoolbox}

\newcommand*{\convertlang}[1]{%
\ifstrequal{#1}{es}{spanish}{%
\ifstrequal{#1}{lat}{latin}{%
\ifstrequal{#1}{eng}{english}{%
\ifstrequal{#1}{deu}{german}{%
\ifstrequal{#1}{enm}{spanish}{% else
% use English as foreign default
english}}}}}}%

\newcommand*{\langtest}[1]{%
\def\temp{\convertlang{#1}}%
\ifstrequal{\temp}{spanish}{True}{False}%
}

\begin{document}

Testing: \convertlang{es} % yields: spanish

\langtest{es} % should yield: True

\end{document}


The command \convertlang takes an ISO 8859 language code (es, eng, etc.) and converts it into a babel language name (spanish, english, etc.). This works fine.

The problem is with \langtest, which compares a string (spanish) with the result of transforming its argument (#1) using \convertlang.

There must be a problem in the expansion of \convertlang and/or of #1 there, buy I don't understand the fundamentals of TeX macro expansion.

I tried doing this:

\newcommand*{\langtest}[1]{%
\edef\temp{\convertlang{#1}}%
\ifstrequal{\temp}{spanish}{True}{False}%
}


expecting that \temp gets the expanded value of its argument. But this doesn't work either.

How can I fix this?

You can use the expandable and expanding string equality test \pdfstrcmp (for portability use \pdf@strcmp from pdftexcmds, see for example Are there any "if" commands like "\ifnum" in LaTeX?). That command expands its test arguments and is itself expandable. (The latter is always nice, the former is not always convenient [cf. https://tex.stackexchange.com/q/230878/35864], but is what we want here.)

\documentclass{article}
\usepackage[utf8]{inputenc}
\usepackage{etoolbox}

\usepackage{pdftexcmds}

\makeatletter
\newcommand*{\IfStrEqualTF}[2]{%
\ifnum\pdf@strcmp{#1}{#2}=\z@
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi}
\makeatother

\newcommand*{\convertlang}[1]{%
\IfStrEqualTF{#1}{es}
{spanish}
{\IfStrEqualTF{#1}{lat}
{latin}
{\IfStrEqualTF{#1}{eng}
{english}
{\IfStrEqualTF{#1}{deu}
{german}
{\IfStrEqualTF{#1}{enm}
{spanish}
{english}}}}}}

\newcommand*{\langtest}[1]{%
\IfStrEqualTF{\convertlang{#1}}{spanish}
{True}
{False}}

\begin{document}
Testing: \convertlang{es} % yields: spanish

\langtest{es} % should yield: True

\langtest{en} % should yield: False
\end{document}


Some more details about the MWE and why it doesn't do the desired thing.

First of all, etoolbox's \ifstrequal is defined with \newrobustcmd. It is therefore robust and not expandable. That means that

\edef\temp{\convertlang{#1}}


does not actually save the long language name in \temp as we would have hoped. It just saves a cascade of \ifstrequal tests. This means that \temp does not contain a simple string.

\edef\temp{\convertlang{es}}%
\show\temp


gives

> \temp=macro:
->\ifstrequal {es}{es}{spanish}{\ifstrequal {es}{lat}{latin}{\ifstrequal {es}{eng}{english}{\ifstrequal {es}{deu}{german}{\ifstrequal {es}{enm}{spanish}{english}}}}}.


You need a string equality test that is expandable to allow \convertlang{#1} to expand to the language name in an \edef.

\pdf@strcmp comes in handy here, since its string comparison is expandable, meaning that a command defined via this equality test can expand to the result of the comparisons in an \edef.

The second issue is that \ifstrequal doesn't expand its arguments, so even if \temp contained only a string, the test wouldn't quite work as intended

\def\temp{spanish}%
\ifstrequal{\temp}{spanish}{True}{False}%


still gives 'False', since \temp is not a string equal to spanish, it expands to a string equal to spanish, that is a small but significant difference.

There are several possible ways around that. Depending on what you can guarantee about \temp it would be enough to say

\expandafter\ifstrequal\expandafter{\temp}{spanish}{True}{False}%


or

\ifdefstring{\temp}{spanish}{True}{False}%


if you know that \temp expands to a string in one expansion step. If more steps are required or you want full expansion, other tricks are needed.

\pdf@strcmp helps here, because it just completely expands its argument. This means that as long as \temp does not contain anything that blows up in an expansion context, its 'ultimate expanded string value' can be compared. You need not worry about expanding it first for the test.

• Thanks. But why does my code not work? – NVaughan Aug 2 '20 at 16:11
• @NVaughan I'm just writing an edit explaining that in more detail. – moewe Aug 2 '20 at 16:12
• Also, I read that pdftexcmds is aimed for LuaTex? – NVaughan Aug 2 '20 at 16:13
• @NVaughan Yes and no. pdftexcmds provides primitives that are not defined in all engines by default in a way that it can be uniformly accessed by all engines. If you are only using pdfTeX you could use \pdfstrcmp but that would fail in LuaTeX. With pdftexcmds we can use \pdf@strcmp, which works everywhere. – moewe Aug 2 '20 at 16:14
• Oh, good to know. Thanks. – NVaughan Aug 2 '20 at 16:17

I wouldn't use long nested if-tests. This is difficult to expand.

\documentclass{article}

\ExplSyntaxOn
\tl_const:Nn \c__nvaughan_convert_es_tl{spanish}
\tl_const:Nn \c__nvaughan_convert_lat_tl{latin}
\tl_const:Nn \c__nvaughan_convert_eng_tl{english}
\tl_const:Nn \c__nvaughan_convert_deu_tl{german}
\tl_const:Nn \c__nvaughan_convert_enm_tl{spanish}

\newcommand*{\convertlang}[1]
{
\tl_if_exist:cTF {c__nvaughan_convert_#1_tl}
{ \tl_use:c {c__nvaughan_convert_#1_tl} }
{ english}
}

\newcommand*{\langtest}[1]{%
\str_if_eq:eeTF {\convertlang{#1}}{spanish}
{True}{False}}
\ExplSyntaxOff
\begin{document}

Testing: \convertlang{es} % yields: spanish
\convertlang{lat} \convertlang{blub}

\langtest{es} % should yield: True

\end{document}


• Thanks @Ulrike Fischer. Where can I find a guide to these LaTeX3 commands? – NVaughan Aug 2 '20 at 17:26
• in interface3.pdf. – Ulrike Fischer Aug 2 '20 at 17:27
• Thanks. I'll give it a look. When you said, "This is difficult to expand", did you mean that it take more processing resources, or that it is prone to faults. – NVaughan Aug 2 '20 at 17:30
• I mean that it is difficult to add more languages as you would have to nest deeper and deeper, while when using commands like above a new language is simply on more line. (It is faster too, as tex doesn't have to do so many comparisions). – Ulrike Fischer Aug 2 '20 at 17:36
• Great. Thanks for your help. – NVaughan Aug 2 '20 at 17:44

I'd use the simpler interface of expl3 with \str_case:nnF

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\NewExpandableDocumentCommand{\convertlang}{m}
{
\nvaughan_convertlang:n { #1 }
}

\cs_new:Nn \nvaughan_convertlang:n
{
\str_case:nnF { #1 }
{
{es}{spanish}
{lat}{latin}
{eng}{english}
{deu}{german}
{enm}{spanish}
}
{english}
}

\NewExpandableDocumentCommand{\langtest}{m}
{
\str_if_eq:eeTF { \nvaughan_convertlang:n { #1 } } { spanish } { True } { False }
}

\ExplSyntaxOff

\begin{document}

Testing: \convertlang{es} % yields: spanish

\langtest{es} should yield: True

\langtest{lat} should yield: False

\end{document}


• Oh gosh! There are so many options! Thanks @egreg. – NVaughan Aug 2 '20 at 19:37