add tag
निरंजन
Have a look at the following example. HOW ON EARTH does `babel` manage to read an internal option as a language-list?

```
% arara: lualatex
% arara: lualatex
% arara: clean: { extensions: [ log,aux,cls ] }
\begin{filecontents}[overwrite]{duck.cls}
\ProvidesClass{duck}
\RequirePackage{expkv-def,expkv-opt}
\newcounter{duck@quack}
\stepcounter{duck@quack}
\def\duck@int@quack#1{%
  \expandafter\def
    \csname
      duck@quack@\alph{duck@quack}%
    \endcsname{#1}%
  \stepcounter{duck@quack}%
}
\NewDocumentCommand\quack{ >{ \SplitList{,} } m }{%
  \ProcessList{#1}{\duck@int@quack}%
}
\ekvdefinekeys{duck}{%
  code    quack          = {%
    \quack{#1}%
  }%
}
\ekvoProcessGlobalOptions{duck}
\LoadClass{article}
\RequirePackage{babel}
\endinput
\end{filecontents}
\documentclass[%
  quack             = {%
    english,spanish,ngerman%
  }%
]{duck}

\begin{document}
\subsubsection*{Macros created by the \textsf{duck} class}

\makeatletter
\begin{itemize}
\item \duck@quack@a
\item \duck@quack@b
\item \duck@quack@c
\end{itemize}
\makeatother

\subsubsection*{Default language call:}

\begin{itemize}
\item \languagename
\end{itemize}
\end{document}
```
Top Answer
Skillmon
The answer to this is quite simple though well hidden.

`babel` uses the old `\@classoptionslist` to parse the class options. That list is subject to the dreaded `\zap@space` mechanism. And `\zap@space` is a really simple macro that removes spaces without any safety nets to not have accidental side effects (keep in mind that this is really old code back from the time when memory was quite expensive and a very sparse resource).

So what does `\zap@space` do and why does it hurt us?

The LaTeX kernel preprocesses the option lists by using
```
\protected@xdef\@classoptionslist{\zap@space#2 \@empty}
```

And `\zap@space` is defined as

```
\def\zap@space#1 #2{%
  #1%
  \ifx#2\@empty\else\expandafter\zap@space\fi
  #2}
```

This is done to remove any spaces from the option lists. However this has two notable side effects. The more obvious one is that everything gets fully expanded inside `\xdef` *after* the spaces were stripped. The less obvious one has to do with the rules how TeX reads its arguments and results in lost braces.

`\zap@space` reads two arguments. The first is delimited by a space. When TeX reads a right-delimited argument it'll drop one level of outer braces around what was read, so with `\def\foo#1\relax{#1}` you'd get `abc` from expanding `\foo{abc}\relax` (but `\foo{abc}d\relax` would yield `{abc}d`). That's one source of potentially dropped braces.

The second brace-loss happens to `#2`, which is read as a normal argument and simply reinserted (so the brace loss is quite obvious). Alas (apart from the very first call) any brace group immediately following a space will lose one set of outer braces, and then if the result of `#2` is a brace group immediately followed by a space it'll lose another set of outer braces (on the first call to `\zap@space` only an input that starts with a brace group that has a space immediately following it would lose a set of braces).

-----

With that out of the way if we look at your option list you have a space immediately in front of your argument to `quack` in the option list, hence what `babel` sees after `\zap@space` will not be your input but the following:

```
quack=english,spanish,ngerman
```

and since neither `spanish` nor `ngerman` are now hidden in braces `babel` will pick those two languages up (but not `english` as that's the argument to the unknown option `quack`).

The very simple solution to this is to make sure your option list doesn't have a space in front of the opening brace. The following would be fine for instance:

```
\documentclass[%
  quack             ={%
    english,spanish,ngerman%
  }%
]{duck}
```

Another possibility is to drop the class options altogether and use a `setup`-macro for your class, though you'd have to pay attention on what to load when and via which hooks in that case, so life will just become more complicated on another end, but it might net you less headaches.

Enter question or answer id or url (and optionally further answer ids/urls from the same question) from

Separate each id/url with a space. No need to list your own answers; they will be imported automatically.