mfirstuc.sty v2.08: sentence and title casing
2022-10-14

The mfirstuc package was originally part of the glossaries bundle (added to glossaries v1.12 in 2008) for use with commands like \Gls, which need to have the first letter converted to uppercase (sentence-case), but as the commands provided by mfirstuc may be used without glossaries, the two were split into separately maintained packages in 2015 (mfirstuc v2.0 and glossaries v4.18).

Version 2.08 has reimplemented \makefirstuc using LaTeX3 commands. If there are any compatibility issues, you can rollback to v2.07:

🖹\usepackage{mfirstuc}[=2.07]


There are two sentence-case commands provided: \MFUsentencecase and \makefirstuc. A summary of the principle features of the two commands is given in Table 1.1.

 Feature \makefirstuc Can expand? ✔ ✖ Supports exclusions? ✔ ✔ Supports blockers? ✖ ✔ Supports mappings? ✖ ✔ Semantic commands must be robust? ✔ ✖

Text only (leading UTF-8 now supported):

\makefirstuc{élite} /
\MFUsentencecase{élite}

Élite / Élite

\makefirstuc{word'} /
\MFUsentencecase{word'}

‘Word’ / ‘Word’

However, if the punctuation character is followed by a blocker or mapping command, the punctuation will need to be excluded otherwise the command will be treated as an exclusion. If possible, use semantic markup instead of directly including the punctuation.

Fragile commands need to be protected with \MFUsentencecase:

🖹\newcommand{\alert}[1]{\textcolor{red}{\textbf{#1}}}
\makefirstuc{\alert{élite}} /
\MFUsentencecase{\protect\alert{élite}}

Élite / Élite

Exclusions are supported by both \makefirstuc and \MFUsentencecase:

🖹\MFUexcl{\index}
\makefirstuc{\index{word}example} /
\MFUsentencecase{\index{word}example}

Example / Example

Blockers are only supported by \makefirstuc. They are treated as exclusions with \MFUsentencecase, which produces a different result:

🖹\MFUblocker{\nameref}
\makefirstuc{\nameref{sec:sentencecase} section} /
\MFUsentencecase{\nameref{sec:sentencecase} section}

Sentence-Case section / Sentence-Case Section

Mappings are only supported by \makefirstuc. They are treated as exclusions with \MFUsentencecase, which produces a different result:

🖹\newrobustcmd{\secref}[1]{section~\ref{#1}}
\newrobustcmd{\Secref}[1]{Section~\ref{#1}}
\MFUaddmap{\secref}{\Secref}
\makefirstuc{\secref{sec:sentencecase} example} /
\MFUsentencecase{\secref{sec:sentencecase} example}

Section 1 example / section 1 Example

Argument expansion: \MFUsentencecase expands its argument and then skips exclusions whereas \makefirstuc parses its argument without expansion and then passes the relevant content to \glsmakefirstuc. Note that different results will occur with the expanded and unexpanded package options. For example:

🖹\usepackage[expanded]{mfirstuc}
\newcommand{\testa}{sample}
\newcommand{\testb}{\testa\ test}
\begin{document}
\makefirstuc{\testb} / \xmakefirstuc{\testb} /
\emakefirstuc{\testb} / \MFUsentencecase{\testb}
\end{document}

Sample test / Sample test / Sample test / Sample test
With the default unexpanded option the result is:
sample test / sample Test / Sample test / Sample test
Note that this is different from the result in earlier versions of mfirstuc.
🖹\usepackage{mfirstuc}[=v2.07]
\newcommand{\testa}{sample}
\newcommand{\testb}{\testa\ test}
\begin{document}
\makefirstuc{\testb} / \xmakefirstuc{\testb} /
\emakefirstuc{\testb}
\end{document}

This produces:
SAMPLE TEST / SAMPLE test / Sample test
If this old result is required, either use rollback or try the grabfirst option.

Argument expansion with mappings (and the default unexpanded option):

🖹\newrobustcmd{\secref}[1]{section~\ref{#1}}
\newrobustcmd{\Secref}[1]{Section~\ref{#1}}
\MFUaddmap{\secref}{\Secref}
\newcommand{\testa}{\secref{sec:sentencecase}}
\newcommand{\testb}{\testa\ example}
\makefirstuc{\testb} / \xmakefirstuc{\testb} /
\emakefirstuc{\testb} / \MFUsentencecase{\testb}

section 1 example / section 1 Example / Section 1 example / section 1 Example

\MFUsentencecase{〈text〉}
This simply uses \text_titlecase_first:n, which is provided by the LaTeX3 kernel. Note that this fully expands the argument. If you use this command, ensure that your semantic commands are robust. For example:
🖹\newrobustcmd{\alert}[1]{\textcolor{red}{\textbf{#1}}}
\MFUsentencecase{\alert{sample} text}

Sample text
Note that the following will fail:
🖹🛇\newcommand{\alert}[1]{\textcolor{red}{\textbf{#1}}}
\MFUsentencecase{\alert{sample} text}

This is because the expansion that’s performed before the case-change will result in:
🖹🛇\textcolor{Red}{\textbf{sample}} text

This triggers an error since Red isn’t a recognised colour name.

\makefirstuc{〈text〉}
This is the original command provided by mfirstuc, which was written to assist sentence-casing in the glossaries package and had to take markup into account. It parses its argument to try to determine which part needs the case-change. The actual case-change is performed by \glsmakefirstuc.

As from v2.08, the definition of \makefirstuc has been updated to use LaTeX3 code, but the argument is still parsed, which allows for non-robust semantic commands, and now also takes blockers and mappings into account. For example:

🖹\newcommand{\alert}[1]{\textcolor{red}{\textbf{#1}}}
\makefirstuc{\alert{sample} text}

Sample text

The rules used when parsing \makefirstuc{〈text〉} are as follows:

1. 1.if text is empty do nothing;

2. 2.if text consists of a single item or starts with a group do \glsmakefirstuc{〈text〉};

3. 3.if text starts with a command cs:

1. (a)if cs is \protect, skip it and continue to the next step where the command cs under inspection is now the one that follows \protect:

2. (b)if cs has been identified as a blocker, just do text (that is, no case-change is applied);

3. (c)if cs isn’t followed by a group then do \glsmakefirstuc{〈text〉};

4. (d)otherwise assume the format is 〈cs〉{〈arg〉}〈following〉 (where following may be empty) and then:

1. i.if cs has been mapped to Cs then do 〈Cs〉{〈arg〉}〈following〉;

2. ii.if cs has been identified as an exclusion, do 〈cs〉{〈arg〉}\makefirstuc{〈following〉};

3. iii.otherwise do 〈cs〉{\makefirstuc{〈arg〉}}〈following〉.

4. 4.otherwise do \glsmakefirstuc{〈text〉}.

Note that the above algorithm uses recursion. The termination points are: don’t implement a change, replace a command with another command, or apply the case-change via \glsmakefirstuc.

For convenience, the following commands are also provided:

\xmakefirstuc{〈text〉}
This is simply a shortcut for:
\expandafter\makefirstuc\expandafter{〈text〉}

This ensures the first token in text is expanded before being passed to \makefirstuc.

\emakefirstuc{〈text〉}
This fully expands its argument before passing it to \makefirstuc. For example:
🖹\newcommand{\test}[1]{sample #1}
\newcommand{\tmp}{\test{text}}
\makefirstuc{\tmp} /
\xmakefirstuc{\tmp} /
\emakefirstuc{\tmp}

sample text / sample Text / Sample text
If you don’t have any mappings or blockers set up, it’s simpler to use \MFUsentencecase instead of \emakefirstuc.

With \emakefirstuc, mappings and blockers will only be detected if they are robust or protected, otherwise they will expand too soon to be detected.

\glsmakefirstuc{〈text〉}
This command is used by \makefirstuc to perform the case-change. The “gls” prefix is for historical reasons as the original code was part of the glossaries package. With the default unexpanded option, this command is defined as:
\MFUsentencecase{\unexpanded{〈text〉}}

The use of \unexpanded inhibits the expansion of text that would otherwise occur. This allows \makefirstuc to maintain as much backward-compatibility with version 2.07 as possible. This is particularly important when using the glossaries package with a style that automatically converts the description to sentence-case, as there may well be documents with complex descriptions that contain fragile commands.

The expanded package option will redefine \glsmakefirstuc to use \MFUsentencecase without \unexpanded. The grabfirst package option will redefine \glsmakefirstuc to use \mfugrabfirstuc to emulates the behaviour of \glsmakefirstuc in v2.07 and below without using rollback.

\mfugrabfirstuc{〈text〉}

avoid where possible
This robust command is provided to emulate v2.07 and its use is discouraged. Note that you may still have different results compared to v2.07, so you may need to use rollback instead. (Note that the grabfirst option additionally redefines \mfirstucMakeUppercase to \MakeUppercase, which you will also need.)

This applies \mfirstucMakeUppercase to the first token in text and leaves the remainder unchanged. This won’t work if text starts with a UTF-8 character unless you use XeLaTeX or LuaLaTeX, so avoid using this method where possible. For example:

🖹\renewcommand{\mfirstucMakeUppercase}[1]{%
\MakeUppercase{#1}%
}
\mfugrabfirstuc{sample}

This is equivalent to:
🖹\mfirstucMakeUppercase{s}ample

Whereas
🗋U\renewcommand{\mfirstucMakeUppercase}[1]{%
\MakeUppercase{#1}%
}
\mfugrabfirstuc{élite}

only works with XeLaTeX or LuaLaTeX.

As with earlier implementations of \makefirstuc that tried to only apply the case-change to the first token, this won’t produce the desired result if the argument starts with a punctuation character.

For example:

🖹\mfugrabfirstuc{sample'}

This is equivalent to:
\mfirstucMakeUppercase{}sample'

so no case-change is applied.

Exclusions will apply to all LaTeX3 case-changing commands, so will also affect \mfirstucMakeUppercase, but this is generally desirable.

\MFUexcl{〈cs〉}
Identifies the text-block command 〈cs〉{〈arg〉} as an exclusion. That is, one that should have its argument excluded from a case-change.

Internally, \MFUexcl adds cs to LaTeX3’s exclusion list \l_text_case_exclude_arg_tl, which means that \MFUsentencecase will skip the command and its argument and apply the case-change to the following content. Some common commands (\begin \cite \end \label and \ref) are automatically added by the LaTeX3 kernel. The exclusions are also recognised by \makefirstuc when it parses its argument.

The command \NoCaseChange is automatically treated as an exclusion by the LaTeX3 case-changing commands with recent LaTeX kernels. You may want to add it as a blocker for \makefirstuc and keep \MFUskippunc for exclusions.

Note that \ensuremath is added as an exclusion by mfirstuc otherwise \makefirstuc will pass its argument to \MFUsentencecase, which will cause a problem.

Another way of identifying content that should be excluded with \makefirstuc is to encapsulate it with:

\MFUskippunc{〈content〉}
This is a robust command that simply does its argument and is automatically added to the exclusion list. This may be used for cases where the excluded command isn’t a simple text-block command that only has one mandatory argument or where leading punctuation is followed by a blocker or mapping.

The command name stems from earlier versions where leading punctuation needed to be skipped. This is no longer necessary, but the command remains as a general purpose robust exclusion command.

For example, \glsadd (provided by glossaries) should have its argument skipped, since its argument is a label, so the following will ensure that \glsadd{〈label〉} will be skipped, and the case-change will be applied to the following text for both \MFUsentencecase and \makefirstuc.

🖹\MFUexcl{\glsadd}
\makefirstuc{\glsadd{ex}some text}
\MFUsentencecase{\glsadd{ex}some text}

This will be equivalent to:
\glsadd{ex}Some text

However, \glsadd can take an optional argument which, if present, will cause a problem. For example:
🖹🛇\MFUexcl{\glsadd}
\makefirstuc{\glsadd[counter=section]{ex}some text}
\MFUsentencecase{\glsadd[counter=section]{ex}some text}

Both commands will cause the following error:
🔎! Package glossaries Error: Glossary entry [' has not been defined.

This is because the open square bracket is assumed to be the argument of \glsadd, so this effectively becomes:
\glsadd{[}Counter=section] exsome text
\glsadd{[}Counter=section] exsome text

which means that now [ is considered the label and everything that follows is just text.

For this situation to work, you need to encapsulate the problematic content with an exclusion command, such as \NoCaseChange or \MFUskippunc:

🖹\makefirstuc{\MFUskippunc{\glsadd[counter=section]{ex}}some text}

This will also work with \MFUsentencecase because mfirstuc automatically adds \MFUskippunc to the exclusion list.

As from glossaries v4.50, \glsadd is automatically added as an exclusion, but be aware of the problem with using the optional argument, as described above.

Punctuation characters are skipped by \MFUsentencecase. For example:

\MFUsentencecase{word'}

‘Word’

However, leading punctuation will result in \makefirstuc passing its entire argument to \MFUsentencecase (since the argument doesn’t start with 〈cs〉{〈arg〉}), which means that \makefirstuc won’t detect any blocker or mapping.

For example:

🖹\MFUblocker{\nameref}
\newcommand*{\qt}[1]{#1''}
\makefirstuc{\nameref{sec:sentencecase} section''} /
\makefirstuc{\MFUskippunc{}\nameref{sec:sentencecase} section''} /
\makefirstuc{\qt{\nameref{sec:sentencecase} section}}

Sentence-Case Section’’ / “Sentence-Case section’’ / Sentence-Case section”
If you are using a package such as csquotes, bear in mind that if the command is followed by an asterisk (a “starred command”) or an optional argument then it won’t fit the expected 〈cs〉{〈arg〉} format and unexpected results will occur.

A blocker is a command that prevents any case-change if it occurs at the start of \makefirstuc or after the argument of an exclusion. Blockers are not supported by \MFUsentencecase but are instead treated as exclusions.

\MFUblocker{〈cs〉}
Identifies cs as a blocker. Since blockers aren’t supported by \MFUsentencecase, this automatically implements \MFUexcl{〈cs〉} to protect its argument from \MFUsentencecase, but it won’t prevent subsequent content from being changed.

For example:

🖹\MFUblocker{\nameref}
\makefirstuc{\nameref{sec:sentencecase} section}

Sentence-Case section

In the following, the blocker isn’t detected by \makefirstuc because the content doesn’t start with 〈cs〉{〈arg〉} or \protect〈cs〉{〈arg〉}. This means that the entire content is passed to \MFUsentencecase which treats \nameref as an exclusion:

🖹\MFUblocker{\nameref}
\makefirstuc{\relax\nameref{sec:sentencecase} section}

Sentence-Case Section

If a blocking command is inside the definition of another command, it won’t be visible to \makefirstuc unless the other command is expanded before applying \makefirstuc. For example, by using \xmakefirstuc or \emakefirstuc. If it doesn’t get expanded until after it has been passed to \MFUsentencecase, then it will be treated as an exclusion instead.

Recent LaTeX kernels provide \NoCaseChange and automatically add it as an exclusion. You may want to add it as a blocker for \makefirstuc. For example:

🖹\makefirstuc{\ensuremath{\alpha}-particle} /
\makefirstuc{$\alpha$-particle} /
\MFUsentencecase{\ensuremath{\alpha}-particle} /
\MFUsentencecase{$\alpha$-particle}

\MFUblocker{\NoCaseChange}%
\makefirstuc{\ensuremath{\alpha}-particle} /
\makefirstuc{\NoCaseChange{$\alpha$}-particle} /
\MFUsentencecase{\ensuremath{\alpha}-particle} /
\MFUsentencecase{\NoCaseChange{$\alpha$}-particle}


\begin826
blockerTable 1.1; 1, 2, 5, 912, 26, 27, 31, 33, 3537

\cite826
csquotes package10

\end826
\ensuremath8, 1226
exclusion commandTable 1.1; 1, 2, 5, 8, 1013, 18, 26, 31, 33, 3637
exclusion word17, 31, 3336

file formats
aux26

glossaries-extra package13
glossaries packagea, 4, 6, 9, 10, 12, 1327
\Glsa, 1327
\gls13, 2627
\glsadd910
\Glspl27
\glspl2627

hyperref package2021

\index2

\label826
lowercase14
\l_text_case_exclude_arg_tl8

\MakeTextUppercase27
\MakeUppercase7, 13, 2728
mappingTable 1.1; 13, 5, 9, 10, 12, 13, 26, 27, 31, 33, 3537
mfirstuc package4

\nameref2, 1011
\NoCaseChange8, 9, 11, 1226

\pdfstringdefDisableCommands21
\protect5
punctuation1, 910

\ref826

semantic command1, 4, 5, 2431
sentence-casea, 1, 6, 12, 14, 32, 3537
\space14, 21, 2324

\texorpdfstring20
textcase package27
\text_titlecase:n14
\text_titlecase_first:n4, 2728
\text_uppercase:n27
title-case14, 31, 32, 33, 3437

\unexpanded`6