5

Please consider the following MWE:

\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?

NVaughan
  • 8,175

3 Answers3

8

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}

enter image description here

egreg
  • 1,121,712
7

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}

enter image description here

Ulrike Fischer
  • 327,261
6

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}

Testing: spanish//True//False


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.

moewe
  • 175,683
  • Thanks. But why does my code not work? – NVaughan Aug 02 '20 at 16:11
  • @NVaughan I'm just writing an edit explaining that in more detail. – moewe Aug 02 '20 at 16:12
  • Also, I read that pdftexcmds is aimed for LuaTex? – NVaughan Aug 02 '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 02 '20 at 16:14
  • Oh, good to know. Thanks. – NVaughan Aug 02 '20 at 16:17
  • Thanks so much. There are things I still don't fully grasp, but the code works, and I can start researching from there. – NVaughan Aug 02 '20 at 16:28
  • @NVaughan feel free to ask small questions here in the comments if the answer is unclear. I have to go now, but I will be happy to address them later. If you have 'bigger' questions, it may be better to ask a new question. – moewe Aug 02 '20 at 16:29