1

I am trying to rewrite my custom class using a cleaner interface. In other posts it was suggested to use the functionality provided around the latex3 project. Comprehensive overview in The LATEX3 Interfaces from CTAN latex3 project.

Say I create two sample documents for two languages and process them (lualatex / texlive 2020): lualatex --shell-escape

\documentclass{scrartcl}
\usepackage{shellesc}

\begin{filecontents}[overwrite]{manual_de.tex}
  \documentclass{scrartcl}

  \usepackage{polyglossia}
  \setdefaultlanguage{german}
  \setkeys{german}{
    spelling=new,
  }

  \usepackage[toc]{blindtext}

  \begin{document}
  \Blinddocument
  \end{document}
\end{filecontents}


\begin{filecontents}[overwrite]{manual_en.tex}
  \documentclass{scrartcl}

  \usepackage{polyglossia}
  \setdefaultlanguage{english}
  \setkeys{english}{
    variant=english,
  }

  \usepackage[toc]{blindtext}

  \begin{document}
  \Blinddocument
  \end{document}
\end{filecontents}

\begin{document}
\ShellEscape{lualatex manual_de.tex}
\ShellEscape{lualatex manual_en.tex}
\end{document}

Now I want to transfer this to a class and change the language by passing an option:

\documentclass{scrartcl}
\usepackage{shellesc}

\begin{filecontents}[overwrite]{wrapper.cls}
  \NeedsTeXFormat{LaTeX2e}
  \RequirePackage{expl3}
  \ProvidesExplClass{wrapper}{2020/05/15}{0.1}{Testclass}

  \keys_define:nn {wrapper}
  { 
    language .default:n = {deutsch} % how to define a variable
  }

  \RequirePackage{polyglossia}

  \str_case:nnF { wrapper.language ??? } % how to access language in wrapper ?
  {
    {deutsch} % wrapper.language == deutsch
    {
      \setdefaultlanguage{german}
      \setkeys{german}{
        spelling=new,
      }
    }
    {english} % wrapper.language == english
    {
      \setdefaultlanguage{english}
      \setkeys{english}{
        variant=english,
      }
    }
  }{\ClassError{wrapper}{unknown language - aborting}}
\end{filecontents}

\begin{filecontents}[overwrite]{useWrapper.tex}
\documentclass{wrapper}

\usepackage[toc]{blindtext}

\begin{document}
\blindtext
\end{document}
\end{filecontents}



\begin{document}
\ShellEscape{lualatex useWrapper.tex}
\end{document}

I am already stuck at two (actualy simple questions):

  • How do i correctly set a variable containing "language"? Is this supposed to be a string or a value:key syntax?
  \keys_define:nn {wrapper}
  { 
    language .default:n = {deutsch} % how to define a variable
  }
  • How do i access this string or key:value construct?
  \str_case:nnF { wrapper.language ??? } % how to access language in wrapper ?

1 Answer 1

3

Inside \keys_define:nn, .default:n is used to say what an option (a key) does when it is passed no value at all, such as \documentclass[language]{wrapper} as opposed to \documentclass[language=english]{wrapper}. What you wanted instead is set with .initial:n; for instance, my code below has:

\str_new:N \g__wrapper_language_str

\keys_define:nn { wrapper }
  {
    language .code:n = { \str_gset:Nn \g__wrapper_language_str {#1} },
    language .initial:n = deutsch,
    language .value_required:n = true,
  }

This defines an str variable and a language option for the wrapper package or class that sets the variable when used (there is another way using .tl_set:N: see how I define the class option in the full code). The option must be passed a value (that is the meaning of the .value_required:n = true). But in order to trigger execution of the above \str_gset:Nn call, \keys_set:nn { wrapper } { ... } must be called with an option list. This can be done with \ProcessKeysOptions from the l3keys2e package:

\ProcessKeysOptions { wrapper }

At this point, you have \g__wrapper_language_str containing the chosen (or default) language. You can therefore use the following to execute code depending on the option value:

\str_case:VnF \g__wrapper_language_str
  {
    { deutsch }
    { ... }
    { english }
    { ... }
  }
  {
    \ClassError { wrapper } { Unknown~language~setting }
      { ... }
  }

The V argument type in \str_case:VnF \g__wrapper_language_str { ... } { ... } says that we pass the value of \g__wrapper_language_str to the base form \str_case:nnF.

This is essentially what you need, but there are a few more subtleties due to how polyglossia works:

  • a class that defines \normalsize, such as article or scrartcl, must be loaded before the \RequirePackage{polyglossia} call;

  • \setdefaultlanguage only works reliably under \ExplSyntaxOff category code régime (try with the german language);

  • \LoadClass expects the class name with category code 11 letters, which wouldn't be the case had I used an str variable to hold the class (thus, I used a tl variable, which is “more general” than the str type—in str variables, every character has catcode 12 except spaces, which have catcode 10).

Full code:

\documentclass{article}
\usepackage{shellesc}

\begin{filecontents}[overwrite]{wrapper.cls}
  \NeedsTeXFormat{LaTeX2e}
  \RequirePackage{expl3}
  \RequirePackage{l3keys2e} % for \ProcessKeysOptions
  \ProvidesExplClass{wrapper}{2020/05/15}{0.1}{Test class}

  \str_new:N \g__wrapper_language_str
  \tl_new:N \g__wrapper_class_tl

  \keys_define:nn { wrapper }
    {
      language .code:n = { \str_gset:Nn \g__wrapper_language_str {#1} },
      language .initial:n = deutsch,
      language .value_required:n = true,
      class    .tl_gset:N = \g__wrapper_class_tl,
      class    .initial:n = scrartcl,
      class    .value_required:n = true,
    }
  \ProcessKeysOptions { wrapper } % process the keys that were passed to us
  \exp_args:NV \LoadClass \g__wrapper_class_tl

  \AtEndOfClass
    {
      \RequirePackage{polyglossia}
      \ExplSyntaxOff % Needed for \setdefaultlanguage to work properly

      \str_case:VnF \g__wrapper_language_str
        {
          { deutsch }
          { \setdefaultlanguage [spelling=new] { german } }
          { english }
          { \setdefaultlanguage [variant=british] { english } }
        }
        {
          \ClassError { wrapper } { Unknown~language~setting }
            {
               Invalid~value~for~the~'language'~option:~
               '\g__wrapper_language_str'.
            }
        }
    }
\end{filecontents}

\begin{filecontents}[overwrite]{useWrapper.tex}
\documentclass[language=english, class=article]{wrapper}
\usepackage[toc]{blindtext}

\begin{document}
\blindtext
\end{document}
\end{filecontents}

\begin{document}
\ShellEscape{lualatex useWrapper.tex}
\end{document}

enter image description here

2
  • 1
    Hey! Incredible. Unfortunately I am still completely overwhelmed with tex programming statements. Nothing here is intuitive, when you come from something else. e.g. Why would I need the \ExplSyntaxOff function? How would somebody figure out hack like that Thanks!
    – Benjamin
    May 15, 2020 at 18:49
  • I used the word "subtleties" for a reason ;-). I didn't know beforehand that the \ExplSyntaxOff would be needed, but when I saw the error message, I understood it was due to the catcode setup. Catcodes are evil, don't you know that? :)
    – frougon
    May 15, 2020 at 20:07

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .