add tag
निरंजन
I am trying to generate small caps with `\textsc` and for that I am using package [`unisc`](https://ctan.org/pkg/unisc), but the problem I am facing here is it isn't working with a very important package in linguistics, i.e., the [`leipzig`](https://ctan.org/pkg/leipzig) package. See the following code.

```
\documentclass{article}
\usepackage{unisc}
\usepackage{leipzig}
\usepackage{fontspec}
\setmainfont{CharisSIL}

\begin{document}
\Acc
\end{document}
```

With LuaTeX-1.15.0; this gives me `Missing \endcsname inserted.` error. In the output I can see `ᴀᴄᴄ@short` (note that the 'ᴀᴄᴄ' part is in small caps). Is the problem with package `unisc` or with package `leipzig`? How to solve this problem?
Top Answer
frougon
I'm afraid `unisc` redefines `\textsc` in a way that places too many restrictions on the argument to work in all situations where the standard `\textsc` works. I'll show how to understand the problem.

Add tracing information to your code:

```
\documentclass{article}
\usepackage{unisc}
\usepackage{leipzig}

\usepackage{fontspec}
\setmainfont{CharisSIL}

\begin{document}

\tracingmacros=1\tracingcommands=2
\Acc
\tracingmacros=0\tracingcommands=0

\end{document}
```

then run `lualatex` on this code:

```
This is LuaHBTeX, Version 1.15.0 (TeX Live 2022) 

...

! Missing \endcsname inserted.
<to be read again> 
\protect 
l.12 \tracingmacros
                 =0\tracingcommands=0
? x
 556 words of node memory still in use:
   6 hlist, 1 vlist, 2 rule, 1 local_par, 1 dir, 1 glue, 4 kern, 1 glyph, 9 att
ribute, 63 glue_spec, 9 attribute_list, 1 temp, 1 if_stack, 3 write nodes
   avail lists: 1:1,2:9,3:1,4:1,5:6,7:5,11:1

warning  (pdf backend): no pages of output.
Transcript written on test_unisc.log.
```

# A little aside

The first thing that may seem puzzling is that the error happens *after* TeX has read (and tokenized) `\tracingmacros` on the line following `\Acc`. This is because after expanding `\Acc` and performing a few operations, TeX finds `\s@gls@hyp@opt` which looks ahead in the input for a possible `[` because `\s@gls@hyp@opt` is defined with `\newcommand*{\s@gls@hyp@opt}[1][]{...}`.

One can reproduce the error in “bare bones” style with the same lookahead phenomenon like so:

```
\documentclass{article}

\newcommand*{\foo}[1][]{\csname\protect}

\begin{document}

\foo
abc

\end{document}
```

which gives:

```
This is LuaHBTeX, Version 1.15.0 (TeX Live 2022) 

...

! Missing \endcsname inserted.
<to be read again> 
\protect 
l.8 a
   bc
? x
 375 words of node memory still in use:
...
```

`\foo` looks ahead in the input for a possible `[` and finds the character token `a` (with catcode 11). So, it sees that the optional argument wasn't passed, calls the internal macro `\\foo` with the empty token list substituted for the optional argument, which results in `\csname\protect`. `\csname` recursively expands tokens until it (hopefully) finds `\endcsname`. However, while doing so, it finds `\protect` which is illegal in this context, because during the recursive expansion started by `\csname`, the only unexpandable tokens allowed are character tokens (cf. TeXbook p. 213).

In normal typesetting, `\protect` is unexpandable because it is `\ifx`-equivalent to the `\relax` primitive. Since `\protect` is not a character token, this is invalid in the context of `\csname ... \endcsname` expansion; therefore, TeX inserts `\endcsname` in the input stream to finish the started `\csname ... \endcsname`, hence the error message.

# From your example to this error

Now, let's see how to discover that your example produces exactly this error, and why. We start from the log file produced by the first code sample in this answer, which is your code with some tracing commands. In this log file, we can see that the error results from the following:

```
\glsentryshort #1->\@gls@entry@field {#1}{short}

#1<-\protect \char "1D00\relax \protect \char "1D04\relax \protect \char "1D04\relax 

\@gls@entry@field #1#2->\csname glo@\glsdetoklabel {#1}@#2\endcsname 

#1<-\protect \char "1D00\relax \protect \char "1D04\relax \protect \char "1D04\relax 

#2<-short
{\csname}

\glsdetoklabel #1->#1

#1<-\protect \char "1D00\relax \protect \char "1D04\relax \protect \char "1D04\relax 

! Missing \endcsname inserted.
<to be read again> 
\protect 
l.12 \tracingmacros
                 =0\tracingcommands=0
```

Here, we see the `\csname` of the error message (the line containing `{\csname}` indicates when TeX expands the `\csname` primitive, which starts recursive expansion until the matching `\endcsname`). We also see that `\glsdetoklabel` simply expands to its argument (like `\@firstofone`), so that the recursive expansion process started by `\csname` then finds `\protect \char "1D00\relax \protect \char "1D04\relax \protect \char "1D04\relax`, hence the error (as explained above).

Now we have to understand why `\glsdetoklabel` received this strange argument. As you can guess, this is what `\textsc{acc}` produces when `\textsc` has been redefined by `unisc`; however, what `glossaries` would need to work properly is `acc` (three character tokens) instead of this big argument—then the `\csname` wouldn't trigger any error.

Since the big argument comes from this:

```
\glsentryshort #1->\@gls@entry@field {#1}{short}

#1<-\protect \char "1D00\relax \protect \char "1D04\relax \protect \char "1D04\relax 
```

the next step is to find how `\glsentryshort` was called. The answer can be found earlier in the log file, before the `pgfparser` processing:

```
\glscustomtext ->\acronymfont {\glsentryshort {acc}}

\acronymfont #1->\leipzigfont {#1}

#1<-\glsentryshort {acc}

\leipzigfont #1->\textsc {#1}

#1<-\glsentryshort {acc}
```

Et voilà. Basically, with the default settings, `\glscustomtext` does `\textsc{\glsentryshort{acc}}`. In normal conditions, `\glsentryshort{acc}` would expand to `acc` (three character tokens), but because of `unisc`'s redefinition of `\textsc` and `\scshape`, `\glsentryshort` sees `\protect \char "1D00\relax \protect \char "1D04\relax \protect \char "1D04\relax` as its argument, because here `pgfparser` leaves `\glsentryshort` as is and processes the three character tokens `acc` in the special way specified by `unisc` after `\scshape`.

Since the `\glsentryshort` macro is expandable (i.e., it works fine in expansion-only contexts; see the annex for more explanations), we can verify this theory by providing a fix that works **in this particular situation:**

```
  \RenewDocumentCommand\textsc{ m }{%
    \begingroup
      \edef\unisc@tmp{\csname text_expand:n\endcsname{#1}}%
      \expandafter\scshape\unisc@tmp
    \endgroup
  }%
```

(modified definition to substitute at the end of `unisc.sty`). This makes your example work but is not good enough in general:

- no fix for `\scshape` (would be harder, as it doesn't take any argument);

- won't work properly for general unexpandable material in the argument of `\textsc` (which is allowed to contain very complex algorithms, built with assignments in particular);

- changes the argument handling of `\textsc` by using `\text_expand:n` first on it (which justifies the previous point and belongs to the same class of problems as the one that triggered your question).

Basically, `unisc` causes `\textsc` and `\scshape` to work in a very particular way, which is more restrictive than what these commands require; and `\Acc` makes this clearly visible...

# Annex: on the word “expandable”

There are some subtleties regarding what “expandable” means depending on what we are talking about.

1. A token is, in a given context, either *expandable* or *unexpandable*. In the former case, one expansion step on the token causes it to be replaced in the input stream by other tokens (after possibly grabbing arguments). Many primitives such as `\relax`, `\def`, `\edef`, `\count`, `\box`, `\hbox`, `\endgraf`, ⟨integer parameter⟩s like `\tracingmacros`, etc., are unexpandable tokens (unless redefined!). If `\foo` has been defined with `\def` or `\newcommand`, it is normally an *expandable token* (but not if preceded with `\noexpand`). Character tokens are unexpandable, except those of category code 13 (“active characters”).

   This is the intuitive meaning of the word “expandable”. By this meaning, a control sequence token which is the name of a macro is “always” expandable (not after `\noexpand`), therefore the concept of an “expandable macro” wouldn't be interesting if it used the intuitive meaning.

2. We say that *macro `\foobar` is expandable* if it performs its job correctly even in expansion-only contexts.

   Generally, macros may need both expansion and execution to perform their duty (mouth and stomach processing in Knuth's analogy). For instance, defining a macro or increasing a counter are assignments and therefore can't be triggered only by expansion (e.g. inside `\edef`, `\write`, `\message`, `\expanded`, `\csname ... \endcsname`, when reading a ⟨number⟩, ⟨dimen⟩ or ⟨glue⟩, after `\romannumeral`, etc.; or by a hit from `\expandafter`). (One exception with LuaTeX: `\directlua` works in expansion-only contexts and can perform assignments.) General macros need the full expansion + execution interleaved process in order to perform their work: we say that they are *not expandable* (this doesn't use the intuitive meaning of “expandable”, of course).
   
   The other macros, that can perform their work as designed even in a context where only expansion takes place, are said to be *expandable*. We can extend this meaning to primitives that do their useful work without requiring anything more than expansion: `\expandafter`, `\csname`, `\the`, `\jobname`, `\ifnum`, `\ifcase`, `\if`, `\ifcat`, `\ifx`, `\ifhmode`, `\ifvmode`, `\ifmmode`, `\else`, `\fi`, `\number`, `\romannumeral` and a few more (see the TeXbook on pages 212 and following).
   
   Expandable macros are very convenient because they can be meaningfully used in many more places than non-expandable ones. In particular, you can “retrieve and store the result” (of the expansion) of an expandable macro using `\edef`.
   
   We sometimes distinguish between “fully expandable” and “restricted-expandable” macros, but I believe this answer is not the right place for diving into this rabbit hole. :-)
   
In certain circumstances, TeX tries to expand tokens; in other circumstances, it doesn't. For instance, when executing the `\edef` in `\edef\foo{...}`, TeX doesn't try to expand `\foo`, but recursively expands tokens inside the braces. Let's see two short examples with `\def`, `\edef` and `\noexpand`.
   
**First example**
      
```
\def\foo{what\bar}
\def\bar{ever}
\edef\tmp{\noexpand\foo\foo}
\show\tmp
```

This prints the following output on the terminal:

```
> \tmp=macro:
->\foo whatever.
l.4 \show\tmp
```

`\noexpand` made the first `\foo` inside the `\edef` temporarily unexpandable; on the other hand, the second `\foo` was recursively expanded.

**Second example**

```
\def\foo{\noexpand\bar}
\edef\tmp{\def\foo{whatever}}
\tmp
\show\bar
```

which prints:

```
> \bar=macro:
->whatever.
l.4 \show\bar
```
   
Here, the `\edef` tries to expand the `\def` token, but it is unexpandable, thus remains `\def`. Expanding `\def` didn't do anything, so the recursive expansion process started by `\edef` continues: the `\foo` token that follows is recursively expanded. This first yields `\noexpand\bar`, then an unexpandable `\bar` token. In the end, since the remaining tokens inside the `\edef` (namely, the character tokens in `{whatever}`) are all unexpandable, after `\edef` has been executed, `\tmp` is a macro whose replacement text is `\def\bar{whatever}`, hence the output.
Answer #2
Ulrike Fischer
The unicode chars you are using are not meant for normal text. To quote wikipedia https://en.wikipedia.org/wiki/Small_caps
(emphasis by me):

> the Unicode standard does define a number of "small capital" characters in the IPA extensions, Phonetic Extensions and Latin Extended-D ranges (0250–02AF, 1D00–1D7F, A720–A7FF).
>These characters, with official names such as latin letter small capital a, are **meant for use in phonetic representations**. For example, ʀ represents a uvular trill. They **should not normally be used in other contexts** (the 'normal' character set should be used with suitable formatting controls as described in the preceding sections). 

The set not even cover all standard letters, there is e.g. no x. So unrelated to technical questions it is semantically wrong to use them with a font command like `\textsc`. 

You should define a command `\ipasmallcaps` or similar that then can be used in appropriate places.  

Answer #3
frougon
Since having Unicode small caps output by `leipzig.sty` seems important to you, I examined the situation in order to find a clean solution. I propose to add a new option to the `leipzig` package, which is named `use-unicode-small-caps-in-short-form` here (I don't mind if the name is changed).

All you need to do is to apply the following patch to `leipzig.sty`. If this is really useful, it could be submitted upstream. Besides, my patch fixes a few bugs in `leipzig.sty` (one of them is a programming error with no consequence; the other ones are real little bugs, AFAICS). Here is the patch (it applies against `leipzig.sty` 2019/10/19 v2.3):

```
diff --git a/leipzig.sty b/leipzig.sty
index 7171e28..bb4163f 100644
--- a/leipzig.sty
+++ b/leipzig.sty
@@ -51,7 +51,14 @@
 \newcommand*{\@leipzig@default@style}{inline}
 \newif\ifleipzighyper\leipzighyperfalse
 \newif\ifleipzignonumbers\leipzignonumberstrue
-\newcommand{\leipzigfont}[1]{\textsc{#1}}%
+\newif\ifleipzig@use@unicode@small@caps@in@short@form % initially false
+\newcommand{\leipzigfont}[1]{%
+  \ifleipzig@use@unicode@small@caps@in@short@form
+    {#1}% no need to use \textsc around Unicode small caps
+  \else
+    \textsc{#1}%
+  \fi
+}%
 \DeclareOption{glossaries}{\leipzig@glossariestrue}
 \DeclareOption{noglossaries}{%
   \leipzig@glossariesfalse
@@ -71,6 +78,9 @@
 }%
 \DeclareOption{leipzighyper}{\leipzighypertrue}
 \DeclareOption{leipzignohyper}{\leipzighyperfalse}
+\DeclareOption{use-unicode-small-caps-in-short-form}{%
+  \leipzig@use@unicode@small@caps@in@short@formtrue
+}
 \DeclareOption*{%
     \PassOptionsToPackage{\CurrentOption}{glossaries}%
 }%
@@ -414,10 +424,62 @@
     }%
   }%
 }{\relax}%
+
+% Unicode small caps in replacement texts (you need to complete this list with
+% the other letters).
+\newcommand*{\leipzig@usc@char@transl@a}{ᴀ}
+\newcommand*{\leipzig@usc@char@transl@c}{ᴄ}
+
+\newcommand*{\leipzig@usc@translate@char}[1]{%
+  \ifcsname leipzig@usc@char@transl@#1\endcsname
+    \unexpanded\expandafter\expandafter\expandafter{%
+      \csname leipzig@usc@char@transl@#1\endcsname
+    }%
+  \else
+    \unexpanded{#1}%
+  \fi
+}
+
+\newcommand*{\leipzig@usc@transform@aux}[1]{%
+  \ifx#1\@nil
+    \expandafter\@gobble                   % eat the second \@nil
+  \else
+    \leipzig@usc@translate@char{#1}%
+    \expandafter\leipzig@usc@transform@aux % recurse
+  \fi
+}
+
+% Apply the transformation to (roughly) each non-space character token c in #1
+% for which \leipzig@usc@char@transl@c is defined.
+\newcommand*{\leipzig@usc@transform}[1]{%
+  \leipzig@usc@transform@aux#1\@nil\@nil
+}
+
+% Depending on \ifleipzig@use@unicode@small@caps@in@short@form, call either
+% #1{#2} or #1{x}, where 'x' stands for the \edef-expansion of
+% \leipzig@usc@transform{#2} (which converts #2 to Unicode small caps).
+% #1 must be a single token.
+\newcommand*{\leipzig@maybe@usc}[2]{%
+  \ifleipzig@use@unicode@small@caps@in@short@form
+    \expandafter\@firstoftwo
+  \else
+    \expandafter\@secondoftwo
+  \fi
+  {%
+    \edef\leipzig@tmp{\leipzig@usc@transform{#2}}%
+    \expandafter#1\expandafter{\leipzig@tmp}%
+  }%
+  {#1{#2}}%
+}
+
 \@ifpackageloaded{glossaries}{%
+  \newcommand{\leipzig@newacronym@aux}[4]{%
+    \newacronym[type=\leipzigtype,#2]{#3}{#1}{#4}%
+  }
+  %
   \newcommand{\newleipzig}[4][]{%
     \bgroup
-    {\newacronym[type=\leipzigtype,#1]{#2}{#3}{#4}}%
+    {\leipzig@maybe@usc\leipzig@newacronym@aux{#3}{#1}{#2}{#4}}% useless group?
     \@newleipzig#2\@nil%
   }%
   \def\@newleipzig#1#2\@nil{%
@@ -459,28 +521,22 @@
 }
 \def\renew@leipzig#1#2{%
   \ifcsname glo@\glsdetoklabel{#2}@name\endcsname
-    \csundef{glo@\glsdetoklabel{#2}@name}
-  \fi
-  \if\relax\detokenize{#2}\relax
-    \expandafter\@firstoftwo
-  \else
-    \expandafter\@secondoftwo
+    \csundef{glo@\glsdetoklabel{#2}@name}%
   \fi
-  {\newleipzig{#2}}{\newleipzig[#1]{#2}}%
+  \newleipzig[#1]{#2}%
 }%
 }%
 {%
-  \newcommand{\newleipzig}[4][]{\@newleipzig(#3)#2\@nil}%
+  \newcommand{\newleipzig}[4][]{\leipzig@maybe@usc\@newleipzig{#3}#2\@nil}%
   \newcommand{\renewleipzig}[4][]{%
     \if@leipzig@defined{#2}
       {%
-       \@newleipzig(#3)#2\@nil%
+       \leipzig@maybe@usc\@newleipzig{#3}#2\@nil
       }%
       {%
        \PackageError{leipzig}
          {Abbreviation `#2' undefined}
          {No `#2` abbreviation is defined, use \string\newleipzig}%
-       \@gobbletwo
       }%
   }
   \def\if@leipzig@defined#1{%
@@ -490,8 +546,8 @@
       \expandafter\@secondoftwo
     \fi
   }
-  \def\@newleipzig(#1)#2#3\@nil{%
-    \uppercase{\expandafter\gdef\csname #2}#3\endcsname{\leipzigfont{#1}}
+  \def\@newleipzig#1#2#3\@nil{%
+    \uppercase{\expandafter\gdef\csname #2}#3\endcsname{\leipzigfont{#1}}%
   }%
 }
 \@ifpackageloaded{glossaries}{%
```

This only does the work for letters `a` and `c` (the only ones used in your MWE), however the implemented machinery is generic: all you need to do is to add the other letters, following the pattern in these two lines:

```
\newcommand*{\leipzig@usc@char@transl@a}{ᴀ}
\newcommand*{\leipzig@usc@char@transl@c}{ᴄ}
```

Also, the above patch **assumes that the short form** of a `\newleipzig` (its second mandatory argument) **never contains spaces**; some modifications would be needed in case this assumption doesn't hold.

To avoid any confusion, I called the modified file `leipzig-modified.sty` on my computer (and `\ProvidesPackage` rightfully warns since I didn't change the package name there); here is a MWE that uses this file (tested with LuaTeX):

```
\documentclass{article}
\usepackage[use-unicode-small-caps-in-short-form]{leipzig-modified}
\usepackage{fontspec}
\setmainfont{CharisSIL}

\begin{document}
ACC \Acc\ \textsc{acc}
\end{document}
```

![image.png](/image?hash=a386826319ea52d19103969f9fa0638183ec40beb55fa95b934e60eb8edcf9ea)

which, after a copy/paste from the PDF, yields `ACC ᴀᴄᴄ acc` (the middle one is composed of Unicode small caps).

Note that my code doesn't use nor test anything from `unisc` or `pgfparser`: it is designed to do what you want for `leipzig.sty`, nothing else. For your other use cases *where you control the input*, I think a dedicated `\unisc` macro as @Skillmon [proposed](https://topanswers.xyz/transcript?room=2052#c139141) would be fine. If you only put non-space character tokens in the argument (or protect spaces with braces as in `\unisc{Foo{ }Bar{ }Baz}`), my `\leipzig@usc@transform` does the job: after

```
\makeatletter
\NewCommandCopy{\unisc}{\leipzig@usc@transform}
\makeatother
```

`\unisc{FooBarBaz}` will output the Unicode small caps in an [expandable way](https://topanswers.xyz/tex?q=2007#a2255), assuming you've defined all used letters with the `\leipzig@usc@char@transl@...` macros (see above). If spaces or more complex stuff needs to be transparently supported in the argument, a `pgfparser`-based solution [as in your other question](https://topanswers.xyz/tex?q=2005#a2252) could do the trick; alternatively, one could probably use some `expl3` function to iterate over the tokens without ignoring space tokens that are not inside braces (or the classical way using `\futurelet`, however this is not expandable, so would require a few changes).

# Separate patches: bug fixes, support for the new option

For easier upstream submission, the proposed changes to `leipzig.sty` can be decomposed into two patches applied in the following order:

1. Bug fixes and simplification:

```
diff --git a/leipzig.sty b/leipzig.sty
index 7171e28..1d46fda 100644
--- a/leipzig.sty
+++ b/leipzig.sty
@@ -459,14 +459,9 @@
 }
 \def\renew@leipzig#1#2{%
   \ifcsname glo@\glsdetoklabel{#2}@name\endcsname
-    \csundef{glo@\glsdetoklabel{#2}@name}
+    \csundef{glo@\glsdetoklabel{#2}@name}%
   \fi
-  \if\relax\detokenize{#2}\relax
-    \expandafter\@firstoftwo
-  \else
-    \expandafter\@secondoftwo
-  \fi
-  {\newleipzig{#2}}{\newleipzig[#1]{#2}}%
+  \newleipzig[#1]{#2}%
 }%
 }%
 {%
@@ -480,7 +475,6 @@
        \PackageError{leipzig}
          {Abbreviation `#2' undefined}
          {No `#2` abbreviation is defined, use \string\newleipzig}%
-       \@gobbletwo
       }%
   }
   \def\if@leipzig@defined#1{%
@@ -491,7 +485,7 @@
     \fi
   }
   \def\@newleipzig(#1)#2#3\@nil{%
-    \uppercase{\expandafter\gdef\csname #2}#3\endcsname{\leipzigfont{#1}}
+    \uppercase{\expandafter\gdef\csname #2}#3\endcsname{\leipzigfont{#1}}%
   }%
 }
 \@ifpackageloaded{glossaries}{%
```

Note: the `\if\relax\detokenize{#2}\relax` was incorrect (`#1` was meant, as far as I understand it), but this didn't trigger any error because passing an empty optional argument to `\newleipzig` doesn't seem to cause any problem... and this is precisely what allows us to simplify this part (as far as I can see, there is no need for the separate code paths).

2. Support for the new option `use-unicode-small-caps-in-short-form`:

```
diff --git a/leipzig.sty b/leipzig.sty
index 1d46fda..bb4163f 100644
--- a/leipzig.sty
+++ b/leipzig.sty
@@ -51,7 +51,14 @@
 \newcommand*{\@leipzig@default@style}{inline}
 \newif\ifleipzighyper\leipzighyperfalse
 \newif\ifleipzignonumbers\leipzignonumberstrue
-\newcommand{\leipzigfont}[1]{\textsc{#1}}%
+\newif\ifleipzig@use@unicode@small@caps@in@short@form % initially false
+\newcommand{\leipzigfont}[1]{%
+  \ifleipzig@use@unicode@small@caps@in@short@form
+    {#1}% no need to use \textsc around Unicode small caps
+  \else
+    \textsc{#1}%
+  \fi
+}%
 \DeclareOption{glossaries}{\leipzig@glossariestrue}
 \DeclareOption{noglossaries}{%
   \leipzig@glossariesfalse
@@ -71,6 +78,9 @@
 }%
 \DeclareOption{leipzighyper}{\leipzighypertrue}
 \DeclareOption{leipzignohyper}{\leipzighyperfalse}
+\DeclareOption{use-unicode-small-caps-in-short-form}{%
+  \leipzig@use@unicode@small@caps@in@short@formtrue
+}
 \DeclareOption*{%
     \PassOptionsToPackage{\CurrentOption}{glossaries}%
 }%
@@ -414,10 +424,62 @@
     }%
   }%
 }{\relax}%
+
+% Unicode small caps in replacement texts (you need to complete this list with
+% the other letters).
+\newcommand*{\leipzig@usc@char@transl@a}{ᴀ}
+\newcommand*{\leipzig@usc@char@transl@c}{ᴄ}
+
+\newcommand*{\leipzig@usc@translate@char}[1]{%
+  \ifcsname leipzig@usc@char@transl@#1\endcsname
+    \unexpanded\expandafter\expandafter\expandafter{%
+      \csname leipzig@usc@char@transl@#1\endcsname
+    }%
+  \else
+    \unexpanded{#1}%
+  \fi
+}
+
+\newcommand*{\leipzig@usc@transform@aux}[1]{%
+  \ifx#1\@nil
+    \expandafter\@gobble                   % eat the second \@nil
+  \else
+    \leipzig@usc@translate@char{#1}%
+    \expandafter\leipzig@usc@transform@aux % recurse
+  \fi
+}
+
+% Apply the transformation to (roughly) each non-space character token c in #1
+% for which \leipzig@usc@char@transl@c is defined.
+\newcommand*{\leipzig@usc@transform}[1]{%
+  \leipzig@usc@transform@aux#1\@nil\@nil
+}
+
+% Depending on \ifleipzig@use@unicode@small@caps@in@short@form, call either
+% #1{#2} or #1{x}, where 'x' stands for the \edef-expansion of
+% \leipzig@usc@transform{#2} (which converts #2 to Unicode small caps).
+% #1 must be a single token.
+\newcommand*{\leipzig@maybe@usc}[2]{%
+  \ifleipzig@use@unicode@small@caps@in@short@form
+    \expandafter\@firstoftwo
+  \else
+    \expandafter\@secondoftwo
+  \fi
+  {%
+    \edef\leipzig@tmp{\leipzig@usc@transform{#2}}%
+    \expandafter#1\expandafter{\leipzig@tmp}%
+  }%
+  {#1{#2}}%
+}
+
 \@ifpackageloaded{glossaries}{%
+  \newcommand{\leipzig@newacronym@aux}[4]{%
+    \newacronym[type=\leipzigtype,#2]{#3}{#1}{#4}%
+  }
+  %
   \newcommand{\newleipzig}[4][]{%
     \bgroup
-    {\newacronym[type=\leipzigtype,#1]{#2}{#3}{#4}}%
+    {\leipzig@maybe@usc\leipzig@newacronym@aux{#3}{#1}{#2}{#4}}% useless group?
     \@newleipzig#2\@nil%
   }%
   \def\@newleipzig#1#2\@nil{%
@@ -465,11 +527,11 @@
 }%
 }%
 {%
-  \newcommand{\newleipzig}[4][]{\@newleipzig(#3)#2\@nil}%
+  \newcommand{\newleipzig}[4][]{\leipzig@maybe@usc\@newleipzig{#3}#2\@nil}%
   \newcommand{\renewleipzig}[4][]{%
     \if@leipzig@defined{#2}
       {%
-       \@newleipzig(#3)#2\@nil%
+       \leipzig@maybe@usc\@newleipzig{#3}#2\@nil
       }%
       {%
        \PackageError{leipzig}
@@ -484,7 +546,7 @@
       \expandafter\@secondoftwo
     \fi
   }
-  \def\@newleipzig(#1)#2#3\@nil{%
+  \def\@newleipzig#1#2#3\@nil{%
     \uppercase{\expandafter\gdef\csname #2}#3\endcsname{\leipzigfont{#1}}%
   }%
 }
```

Unless the package author knows a good reason that I ignore, the outer braces where I commented “useless group?” can be removed.

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.