% \iffalse meta-comment
%
% File: zref-clever.dtx
%
% This file is part of the LaTeX package "zref-clever".
%
% Copyright (C) 2021-2023  Gustavo Barros
%
% It may be distributed and/or modified under the conditions of the
% LaTeX Project Public License (LPPL), either version 1.3c of this
% license or (at your option) any later version.  The latest version
% of this license is in the file:
%
%    https://www.latex-project.org/lppl.txt
%
% and version 1.3 or later is part of all distributions of LaTeX
% version 2005/12/01 or later.
%
%
% This work is "maintained" (as per LPPL maintenance status) by
% Gustavo Barros.
%
% This work consists of the files zref-clever.dtx,
%                                 zref-clever.ins,
%                                 zref-clever.tex,
%                                 zref-clever-code.tex,
%                   and the files generated from them.
%
% The released version of this package is available from CTAN.
%
% -----------------------------------------------------------------------
%
% The development version of the package can be found at
%
%    https://github.com/gusbrs/zref-clever
%
% for those people who are interested.
%
% -----------------------------------------------------------------------
%
% \fi
%
% \iffalse
%<*driver>
\documentclass{l3doc}

% Have \GetFileInfo pick up date and version data and used in the
% documentation.
\usepackage{zref-clever}
\zcsetup{cap,nameinlink=false}
\usepackage{zref-check}
\usepackage{zref-titleref}

\begin{document}

\DocInput{zref-clever.dtx}

\end{document}
%</driver>
% \fi
%
% \DoNotIndex{\\,\{,\}}
% \DoNotIndex{\c@,\cl@,\c@enumN,\p@}
% \DoNotIndex{\the}
%
% \NewDocumentCommand\githubissue{m}{^^A
%   issue~\href{https://github.com/gusbrs/zref-clever/issues/#1}{\##1}}
%
% \NewDocumentCommand\githubPR{m}{^^A
%   PR~\href{https://github.com/gusbrs/zref-clever/pull/#1}{\##1}}
%
% \NewDocumentCommand\contributor{m}{#1}
% \NewDocumentCommand\username{m}{`\texttt{#1}'}
%
% \NewDocumentCommand\opt{m}{\texttt{#1}}
%
% \pdfstringdefDisableCommands{^^A
%   \def\opt#1{#1}
% }
%
% ^^A Have the Index at 'section' level rather than 'part'.  Otherwise it is
% ^^A just the same definition from 'l3doc.cls'.
% \IndexPrologue{^^A
%   \section*{Index}
%   \markboth{Index}{Index}
%   \addcontentsline{toc}{section}{Index}
%   The italic numbers denote the pages where the corresponding entry is
%   described, numbers underlined point to the definition, all others indicate
%   the places where it is used.^^A
% }
%
%
% \GetFileInfo{zref-clever.sty}
%
% \title{^^A
%   The \pkg{zref-clever} package^^A
%   \thanks{This file describes \fileversion, released \filedate.}^^A
%   \texorpdfstring{\\{}\medskip{}}{ - }^^A
%   Code documentation^^A
%   \texorpdfstring{\medskip{}}{}^^A
% }
%
% \author{^^A
%   Gustavo Barros^^A
%   \thanks{\url{https://github.com/gusbrs/zref-clever}}^^A
% }
%
% \date{\filedate}
%
% \maketitle
%
% \begin{center}
%   \textbf{EXPERIMENTAL}
% \end{center}
%
%
% \tableofcontents
%
%
% \section{Initial setup}
%
% Start the \pkg{DocStrip} guards.
%    \begin{macrocode}
%<*package>
%    \end{macrocode}
%
% Identify the internal prefix (\LaTeX3 \pkg{DocStrip} convention).
%    \begin{macrocode}
%<@@=zrefclever>
%    \end{macrocode}
%
% Taking a stance on backward compatibility of the package.  During initial
% development, we have used freely recent features of the kernel (albeit
% refraining from \pkg{l3candidates}, even though I'd have loved to have used
% \cs{bool_case_true:}\dots{}).  We presume \pkg{xparse} (which made to the
% kernel in the 2020-10-01 release), and \pkg{expl3} as well (which made to
% the kernel in the 2020-02-02 release).  We also just use UTF-8 for the
% language files (which became the default input encoding in the 2018-04-01
% release).  Finally, a couple of changes came with the 2021-11-15 kernel
% release, which are important here.  First, a fix was made to the new hook
% management system (\pkg{ltcmdhooks}), with implications to the hook we add
% to \cs{appendix} (by \contributor{Phelype Oleinik} at
% \url{https://tex.stackexchange.com/q/617905} and
% \url{https://github.com/latex3/latex2e/pull/699}).  Second, the support for
% \cs{@currentcounter} has been improved, including \cs{footnote} and
% \pkg{amsmath} (by \contributor{Frank Mittelbach} and \contributor{Ulrike
% Fischer} at \url{https://github.com/latex3/latex2e/issues/687}).  Hence,
% since we would not be able to go much backwards without special handling
% anyway, we make the cut at the 2021-11-15 kernel release.
%
%    \begin{macrocode}
\providecommand\IfFormatAtLeastTF{\@ifl@t@r\fmtversion}
\IfFormatAtLeastTF{2021-11-15}
  {}
  {%
    \PackageError{zref-clever}{LaTeX kernel too old}
      {%
        'zref-clever' requires a LaTeX kernel 2021-11-15 or newer.%
        \MessageBreak Loading will abort!%
      }%
    \endinput
  }%
%    \end{macrocode}
%
%
% Identify the package.
%    \begin{macrocode}
\ProvidesExplPackage {zref-clever} {2023-02-13} {0.3.4}
  {Clever LaTeX cross-references based on zref}
%    \end{macrocode}
%
%
% \section{Dependencies}
%
% Required packages.  Besides these, \pkg{zref-hyperref} may also be loaded
% depending on user options.  \pkg{zref-clever} also requires UTF-8 input
% encoding (see discussion with \contributor{David Carlisle} at
% \url{https://chat.stackexchange.com/transcript/message/62644791#62644791}).
%
%    \begin{macrocode}
\RequirePackage { zref-base }
\RequirePackage { zref-user }
\RequirePackage { zref-abspage }
\RequirePackage { ifdraft }
%    \end{macrocode}
%
%
% \section{\pkg{zref} setup}
%
% For the purposes of the package, we need to store some information with the
% labels, some of it standard, some of it not so much.  So, we have to setup
% \pkg{zref} to do so.
%
% Some basic properties are handled by \pkg{zref} itself, or some of its
% modules.  The \texttt{default} and \texttt{page} properties are provided by
% \pkg{zref-base}, while \pkg{zref-abspage} provides the \texttt{abspage}
% property which gives us a safe and easy way to sort labels for page
% references.
%
% The \texttt{counter} property, in most cases, will be just the kernel's
% \cs{@currentcounter}, set by \cs{refstepcounter}.  However, not everywhere
% is it assured that \cs{@currentcounter} gets updated as it should, so we
% need to have some means to manually tell \pkg{zref-clever} what the current
% counter actually is.  This is done with the \opt{currentcounter} option, and
% stored in \cs{l_@@_current_counter_tl}, whose default is
% \cs{@currentcounter}.
%
%    \begin{macrocode}
\zref@newprop { zc@counter } { \l_@@_current_counter_tl }
\zref@addprop \ZREF@mainlist { zc@counter }
%    \end{macrocode}
%
% The reference itself, stored by \pkg{zref-base} in the \texttt{default}
% property, is somewhat a disputed real estate.  In particular, the use of
% \cs{labelformat} (previously from \pkg{varioref}, now in the kernel) will
% include there the reference ``prefix'' and complicate the job we are trying
% to do here.  Hence, we isolate \cs[no-index]{the}\meta{counter} and store it
% ``clean'' in \texttt{thecounter} for reserved use.  Since
% \cs{@currentlabel}, which populates the \texttt{default} property, is
% \emph{more reliable} than \cs{@currentcounter}, \texttt{thecounter} is meant
% to be kept as an \emph{option} (\opt{ref} option), in case there's need to
% use \pkg{zref-clever} together with \cs{labelformat}.  Based on the
% definition of \cs{@currentlabel} done inside \cs{refstepcounter} in
% \texttt{texdoc source2e}, section \texttt{ltxref.dtx}.  We just drop the
% \cs[no-index]{p@...}  prefix.
%
%    \begin{macrocode}
\zref@newprop { thecounter }
  {
    \cs_if_exist:cTF { c@ \l_@@_current_counter_tl }
      { \use:c { the \l_@@_current_counter_tl } }
      {
        \cs_if_exist:cT { c@ \@currentcounter }
          { \use:c { the \@currentcounter } }
      }
  }
\zref@addprop \ZREF@mainlist { thecounter }
%    \end{macrocode}
%
%
% Much of the work of \pkg{zref-clever} relies on the association between a
% label's ``counter'' and its ``type'' (see the User manual section on
% ``Reference types'').  Superficially examined, one might think this relation
% could just be stored in a global property list, rather than in the label
% itself.  However, there are cases in which we want to distinguish different
% types for the same counter, depending on the document context.  Hence, we
% need to store the ``type'' of the ``counter'' for each ``label''.  In
% setting this, the presumption is that the label's type has the same name as
% its counter, unless it is specified otherwise by the \opt{countertype}
% option, as stored in \cs{l_@@_counter_type_prop}.
%
%    \begin{macrocode}
\zref@newprop { zc@type }
  {
    \tl_if_empty:NTF \l_@@_reftype_override_tl
      {
        \exp_args:NNe \prop_if_in:NnTF \l_@@_counter_type_prop
          \l_@@_current_counter_tl
          {
            \exp_args:NNe \prop_item:Nn \l_@@_counter_type_prop
              { \l_@@_current_counter_tl }
          }
          { \l_@@_current_counter_tl }
      }
      { \l_@@_reftype_override_tl }
  }
\zref@addprop \ZREF@mainlist { zc@type }
%    \end{macrocode}
%
%
% Since the \texttt{default}/\texttt{thecounter} and \texttt{page} properties
% store the ``\emph{printed} representation'' of their respective counters,
% for sorting and compressing purposes, we are also interested in their
% numeric values.  So we store them in \texttt{zc@cntval} and
% \texttt{zc@pgval}.  For this, we use \cs[no-index]{c@}\meta{counter}, which
% contains the counter's numerical value (see `texdoc source2e', section
% `ltcounts.dtx').
%    \begin{macrocode}
\zref@newprop { zc@cntval } [0]
  {
    \cs_if_exist:cTF { c@ \l_@@_current_counter_tl }
      { \int_use:c { c@ \l_@@_current_counter_tl } }
      {
        \cs_if_exist:cT { c@ \@currentcounter }
          { \int_use:c { c@ \@currentcounter } }
      }
  }
\zref@addprop \ZREF@mainlist { zc@cntval }
\zref@newprop* { zc@pgval } [0] { \int_use:c { c@page } }
\zref@addprop \ZREF@mainlist { zc@pgval }
%    \end{macrocode}
%
%
% However, since many counters (may) get reset along the document, we require
% more than just their numeric values.  We need to know the reset chain of a
% given counter, in order to sort and compress a group of references.  Also
% here, the ``printed representation'' is not enough, not only because it is
% easier to work with the numeric values but, given we occasionally group
% multiple counters within a single type, sorting this group requires to know
% the actual counter reset chain.
%
% Furthermore, even if it is true that most of the definitions of counters,
% and hence of their reset behavior, is likely to be defined in the preamble,
% this is not necessarily true.  Users can create counters, newtheorems
% mid-document, and alter their reset behavior along the way.  Was that not
% the case, we could just store the desired information at
% \texttt{begindocument} in a variable and retrieve it when needed.  But since
% it is, we need to store the information with the label, with the values as
% current when the label is set.
%
% Though counters can be reset at any time, and in different ways at that, the
% most important use case is the automatic resetting of counters when some
% other counter is stepped, as performed by the standard mechanisms of the
% kernel (optional argument of \cs{newcounter}, \cs{@addtoreset},
% \cs{counterwithin}, and related infrastructure).  The canonical optional
% argument of \cs{newcounter} establishes that the counter being created (the
% mandatory argument) gets reset every time the ``enclosing counter'' gets
% stepped (this is called in the usual sources ``within-counter'', ``old
% counter'', ``supercounter'', ``parent counter'' etc.).  This information is
% somewhat tricky to get.  For starters, the counters which may reset the
% current counter are not retrievable from the counter itself, because this
% information is stored with the counter that does the resetting, not with the
% one that gets reset (the list is stored in \cs[no-index]{cl@}\meta{counter}
% with format
% \texttt{\cs{@elt}\{countera\}\cs{@elt}\{counterb\}\cs{@elt}\{counterc\}},
% see \file{ltcounts.dtx} in \texttt{texdoc source2e}).  Besides, there may be
% a chain of resetting counters, which must be taken into account: if
% \texttt{counterC} gets reset by \texttt{counterB}, and \texttt{counterB}
% gets reset by \texttt{counterA}, stepping the latter affects all three of
% them.
%
% The procedure below examines a set of counters, those in
% \cs{l_@@_counter_resetters_seq}, and for each of them retrieves the set of
% counters it resets, as stored in \cs[no-index]{cl@}\meta{counter}, looking
% for the counter for which we are trying to set a label
% (\cs{l_@@_current_counter_tl}, by default \cs{@currentcounter}, passed as an
% argument to the functions).  There is one relevant caveat to this procedure:
% \cs{l_@@_counter_resetters_seq} is populated by hand with the ``usual
% suspects'', there is no way (that I know of) to ensure it is exhaustive.
% However, it is not that difficult to create a reasonable ``usual suspects''
% list which, of course, should include the counters for the sectioning
% commands to start with, and it is easy to add more counters to this list if
% needed, with the option \opt{counterresetters}.  Unfortunately, not all
% counters are created alike, or reset alike.  Some counters, even some kernel
% ones, get reset by other mechanisms (notably, the \texttt{enumerate}
% environment counters do not use the regular counter machinery for resetting
% on each level, but are nested nevertheless by other means).  Therefore,
% inspecting \cs[no-index]{cl@}\meta{counter} cannot possibly fully account
% for all of the automatic counter resetting which takes place in the
% document.  And there's also no other ``general rule'' we could grab on for
% this, as far as I know.  So we provide a way to manually tell
% \pkg{zref-clever} of these cases, by means of the \opt{counterresetby}
% option, whose information is stored in \cs{l_@@_counter_resetby_prop}.  This
% manual specification has precedence over the search through
% \cs{l_@@_counter_resetters_seq}, and should be handled with care, since
% there is no possible verification mechanism for this.
%
%
% \begin{macro}[EXP]{\@@_get_enclosing_counters_value:n}
%   Recursively generate a \emph{sequence} of ``enclosing counters'' values,
%   for a given \meta{counter} and leave it in the input stream.  This
%   function must be expandable, since it gets called from \cs{zref@newprop}
%   and is the one responsible for generating the desired information when
%   the label is being set.  Note that the order in which we are getting this
%   information is reversed, since we are navigating the counter reset chain
%   bottom-up.  But it is very hard to do otherwise here where we need
%   expandable functions, and easy to handle at the reading side.
%     \begin{syntax}
%       \cs{@@_get_enclosing_counters_value:n} \Arg{counter}
%     \end{syntax}
%    \begin{macrocode}
\cs_new:Npn \@@_get_enclosing_counters_value:n #1
  {
    \cs_if_exist:cT { c@ \@@_counter_reset_by:n {#1} }
      {
        { \int_use:c { c@ \@@_counter_reset_by:n {#1} } }
        \@@_get_enclosing_counters_value:e
          { \@@_counter_reset_by:n {#1} }
      }
  }
%    \end{macrocode}
%
% Both \texttt{e} and \texttt{f} expansions work for this particular recursive
% call.  I'll stay with the \texttt{e} variant, since conceptually it is what
% I want (\texttt{x} itself is not expandable), and this package is anyway not
% compatible with older kernels for which the performance penalty of the
% \texttt{e} expansion would ensue (helpful comment by \contributor{Enrico
% Gregorio}, aka `egreg' at
% \url{https://tex.stackexchange.com/q/611370/#comment1529282_611385}).
%    \begin{macrocode}
\cs_generate_variant:Nn \@@_get_enclosing_counters_value:n { e }
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}[EXP]{\@@_counter_reset_by:n}
%   Auxiliary function for \cs{@@_get_enclosing_counters_value:n}, and useful
%   on its own standing.  It is broken in parts to be able to use the
%   expandable mapping functions.  \cs{@@_counter_reset_by:n} leaves in the
%   stream the ``enclosing counter'' which resets \meta{counter}.
%   \begin{syntax}
%     \cs{@@_counter_reset_by:n} \Arg{counter}
%   \end{syntax}
%    \begin{macrocode}
\cs_new:Npn \@@_counter_reset_by:n #1
  {
    \bool_if:nTF
      { \prop_if_in_p:Nn \l_@@_counter_resetby_prop {#1} }
      { \prop_item:Nn \l_@@_counter_resetby_prop {#1} }
      {
        \seq_map_tokens:Nn \l_@@_counter_resetters_seq
          { \@@_counter_reset_by_aux:nn {#1} }
      }
  }
\cs_new:Npn \@@_counter_reset_by_aux:nn #1#2
  {
    \cs_if_exist:cT { c@ #2 }
      {
        \tl_if_empty:cF { cl@ #2 }
          {
            \tl_map_tokens:cn { cl@ #2 }
              { \@@_counter_reset_by_auxi:nnn {#2} {#1} }
          }
      }
  }
\cs_new:Npn \@@_counter_reset_by_auxi:nnn #1#2#3
  {
    \str_if_eq:nnT {#2} {#3}
      { \tl_map_break:n { \seq_map_break:n {#1} } }
  }
%    \end{macrocode}
% \end{macro}
%
%
% Finally, we create the \texttt{zc@enclval} property, and add it to the
% \texttt{main} property list.
%    \begin{macrocode}
\zref@newprop { zc@enclval }
  {
    \@@_get_enclosing_counters_value:e
      \l_@@_current_counter_tl
  }
\zref@addprop \ZREF@mainlist { zc@enclval }
%    \end{macrocode}
%
%
% Another piece of information we need is the page numbering format being used
% by \cs{thepage}, so that we know when we can (or not) group a set of page
% references in a range.  Unfortunately, \texttt{page} is not a typical
% counter in ways which complicates things.  First, it does commonly get reset
% along the document, not necessarily by the usual counter reset chains, but
% rather with \cs{pagenumbering} or variations thereof.  Second, the format of
% the page number commonly changes in the document (roman, arabic, etc.), not
% necessarily, though usually, together with a reset.  Trying to ``parse''
% \cs{thepage} to retrieve such information is bound to go wrong: we don't
% know, and can't know, what is within that macro, and that's the business of
% the user, or of the documentclass, or of the loaded packages.  The technique
% used by \pkg{cleveref}, which we borrow here, is simple and smart: store
% with the label what \cs{thepage} would return, if the counter \cs{c@page}
% was ``\(1\)''.  That does not allow us to \emph{sort} the references,
% luckily however, we have \texttt{abspage} which solves this problem.  But we
% can decide whether two labels can be compressed into a range or not based on
% this format: if they are identical, we can compress them, otherwise, we
% can't.  To do so, we locally set \cs{c@page} to ``1'', thus avoiding any
% global spillovers of this trick.  Since this operation is not expandable we
% cannot run it directly from the property definition.  Hence, we use a
% shipout hook, and set \cs{g_@@_page_format_tl}, which can then be retrieved
% by the starred definition of \texttt{\cs{zref@newprop}*\{zc@pgfmt\}}.
%
%    \begin{macrocode}
\tl_new:N \g_@@_page_format_tl
\AddToHook { shipout / before }
  {
    \group_begin:
    \int_set:Nn \c@page { 1 }
    \tl_gset:Nx \g_@@_page_format_tl { \thepage }
    \group_end:
  }
\zref@newprop* { zc@pgfmt } { \g_@@_page_format_tl }
\zref@addprop \ZREF@mainlist { zc@pgfmt }
%    \end{macrocode}
%
%
% Still some other properties which we don't need to handle at the data
% provision side, but need to cater for at the retrieval side, are the ones
% from the \pkg{zref-xr} module, which are added to the labels imported from
% external documents, and needed to construct hyperlinks to them and to
% distinguish them from the current document ones at sorting and compressing:
% \texttt{urluse}, \texttt{url} and \texttt{externaldocument}.
%
%
%
% \section{Plumbing}
%
%
% \subsection{Auxiliary}
%
%
% \begin{macro}
%   {
%     \@@_if_package_loaded:n ,
%     \@@_if_class_loaded:n ,
%   }
%   Just a convenience, since sometimes we just need one of the branches, and
%   it is particularly easy to miss the empty F branch after a long T one.
%    \begin{macrocode}
\prg_new_conditional:Npnn \@@_if_package_loaded:n #1 { T , F , TF }
  { \IfPackageLoadedTF {#1} { \prg_return_true: } { \prg_return_false: } }
\prg_new_conditional:Npnn \@@_if_class_loaded:n #1 { T , F , TF }
  { \IfClassLoadedTF {#1} { \prg_return_true: } { \prg_return_false: } }
%    \end{macrocode}
% \end{macro}
%
%
% \subsection{Messages}
%
%
%    \begin{macrocode}
\msg_new:nnn { zref-clever } { option-not-type-specific }
  {
    Option~'#1'~is~not~type-specific~\msg_line_context:.~
    Set~it~in~'\iow_char:N\\zcLanguageSetup'~before~first~'type'~
    switch~or~as~package~option.
  }
\msg_new:nnn { zref-clever } { option-only-type-specific }
  {
    No~type~specified~for~option~'#1'~\msg_line_context:.~
    Set~it~after~'type'~switch.
  }
\msg_new:nnn { zref-clever } { key-requires-value }
  { The~'#1'~key~'#2'~requires~a~value~\msg_line_context:. }
\msg_new:nnn { zref-clever } { language-declared }
  { Language~'#1'~is~already~declared~\msg_line_context:.~Nothing~to~do. }
\msg_new:nnn { zref-clever } { unknown-language-alias }
  {
    Language~'#1'~is~unknown~\msg_line_context:.~Can't~alias~to~it.~
    See~documentation~for~'\iow_char:N\\zcDeclareLanguage'~and~
    '\iow_char:N\\zcDeclareLanguageAlias'.
  }
\msg_new:nnn { zref-clever } { unknown-language-setup }
  {
    Language~'#1'~is~unknown~\msg_line_context:.~Can't~set~it~up.~
    See~documentation~for~'\iow_char:N\\zcDeclareLanguage'~and~
    '\iow_char:N\\zcDeclareLanguageAlias'.
  }
\msg_new:nnn { zref-clever } { unknown-language-opt }
  {
    Language~'#1'~is~unknown~\msg_line_context:.~
    See~documentation~for~'\iow_char:N\\zcDeclareLanguage'~and~
    '\iow_char:N\\zcDeclareLanguageAlias'.
  }
\msg_new:nnn { zref-clever } { unknown-language-decl }
  {
    Can't~set~declension~'#1'~for~unknown~language~'#2'~\msg_line_context:.~
    See~documentation~for~'\iow_char:N\\zcDeclareLanguage'~and~
    '\iow_char:N\\zcDeclareLanguageAlias'.
  }
\msg_new:nnn { zref-clever } { language-no-decl-ref }
  {
    Language~'#1'~has~no~declared~declension~cases~\msg_line_context:.~
    Nothing~to~do~with~option~'d=#2'.
  }
\msg_new:nnn { zref-clever } { language-no-gender }
  {
    Language~'#1'~has~no~declared~gender~\msg_line_context:.~
    Nothing~to~do~with~option~'#2=#3'.
  }
\msg_new:nnn { zref-clever } { language-no-decl-setup }
  {
    Language~'#1'~has~no~declared~declension~cases~\msg_line_context:.~
    Nothing~to~do~with~option~'case=#2'.
  }
\msg_new:nnn { zref-clever } { unknown-decl-case }
  {
    Declension~case~'#1'~unknown~for~language~'#2'~\msg_line_context:.~
    Using~default~declension~case.
  }
\msg_new:nnn { zref-clever } { nudge-multitype }
  {
    Reference~with~multiple~types~\msg_line_context:.~
    You~may~wish~to~separate~them~or~review~language~around~it.
  }
\msg_new:nnn { zref-clever } { nudge-comptosing }
  {
    Multiple~labels~have~been~compressed~into~singular~type~name~
    for~type~'#1'~\msg_line_context:.
  }
\msg_new:nnn { zref-clever } { nudge-plural-when-sg }
  {
    Option~'sg'~signals~that~a~singular~type~name~was~expected~
    \msg_line_context:.~But~type~'#1'~has~plural~type~name.
  }
\msg_new:nnn { zref-clever } { gender-not-declared }
  { Language~'#1'~has~no~'#2'~gender~declared~\msg_line_context:. }
\msg_new:nnn { zref-clever } { nudge-gender-mismatch }
  {
    Gender~mismatch~for~type~'#1'~\msg_line_context:.~
    You've~specified~'g=#2'~but~type~name~is~'#3'~for~language~'#4'.
  }
\msg_new:nnn { zref-clever } { nudge-gender-not-declared-for-type }
  {
    You've~specified~'g=#1'~\msg_line_context:.~
    But~gender~for~type~'#2'~is~not~declared~for~language~'#3'.
  }
\msg_new:nnn { zref-clever } { nudgeif-unknown-value }
  { Unknown~value~'#1'~for~'nudgeif'~option~\msg_line_context:. }
\msg_new:nnn { zref-clever } { option-document-only }
  { Option~'#1'~is~only~available~after~\iow_char:N\\begin\{document\}. }
\msg_new:nnn { zref-clever } { langfile-loaded }
  { Loaded~'#1'~language~file. }
\msg_new:nnn { zref-clever } { zref-property-undefined }
  {
    Option~'ref=#1'~requested~\msg_line_context:.~
    But~the~property~'#1'~is~not~declared,~falling-back~to~'default'.
  }
\msg_new:nnn { zref-clever } { endrange-property-undefined }
  {
    Option~'endrange=#1'~requested~\msg_line_context:.~
    But~the~property~'#1'~is~not~declared,~'endrange'~not~set.
  }
\msg_new:nnn { zref-clever } { hyperref-preamble-only }
  {
    Option~'hyperref'~only~available~in~the~preamble~\msg_line_context:.~
    To~inhibit~hyperlinking~locally,~you~can~use~the~starred~version~of~
    '\iow_char:N\\zcref'.
  }
\msg_new:nnn { zref-clever } { missing-hyperref }
  { Missing~'hyperref'~package.~Setting~'hyperref=false'. }
\msg_new:nnn { zref-clever } { option-preamble-only }
  { Option~'#1'~only~available~in~the~preamble~\msg_line_context:. }
\msg_new:nnn { zref-clever } { unknown-compat-module }
  {
    Unknown~compatibility~module~'#1'~given~to~option~'nocompat'.~
    Nothing~to~do.
  }
\msg_new:nnn { zref-clever } { refbounds-must-be-four }
  {
    The~value~of~option~'#1'~must~be~a~comma~sepatared~list~
    of~four~items.~We~received~'#2'~items~\msg_line_context:.~
    Option~not~set.
  }
\msg_new:nnn { zref-clever } { missing-zref-check }
  {
    Option~'check'~requested~\msg_line_context:.~
    But~package~'zref-check'~is~not~loaded,~can't~run~the~checks.
  }
\msg_new:nnn { zref-clever } { zref-check-too-old }
  {
    Option~'check'~requested~\msg_line_context:.~
    But~'zref-check'~newer~than~'#1'~is~required,~can't~run~the~checks.
  }
\msg_new:nnn { zref-clever } { missing-type }
  { Reference~type~undefined~for~label~'#1'~\msg_line_context:. }
\msg_new:nnn { zref-clever } { missing-property }
  { Reference~property~'#1'~undefined~for~label~'#2'~\msg_line_context:. }
\msg_new:nnn { zref-clever } { missing-name }
  { Reference~format~option~'#1'~undefined~for~type~'#2'~\msg_line_context:. }
\msg_new:nnn { zref-clever } { single-element-range }
  { Range~for~type~'#1'~resulted~in~single~element~\msg_line_context:. }
\msg_new:nnn { zref-clever } { compat-package }
  { Loaded~support~for~'#1'~package. }
\msg_new:nnn { zref-clever } { compat-class }
  { Loaded~support~for~'#1'~documentclass. }
\msg_new:nnn { zref-clever } { option-deprecated }
  {
    Option~'#1'~has~been~deprecated~\msg_line_context:.\iow_newline:
    Use~'#2'~instead.
  }
\msg_new:nnn { zref-clever } { load-time-options }
  {
    'zref-clever'~does~not~accept~load-time~options.~
    To~configure~package~options,~use~'\iow_char:N\\zcsetup'.
  }
%    \end{macrocode}
%
%
% \subsection{Data extraction}
%
%
% \begin{macro}{\@@_extract_default:Nnnn}
%   Extract property \meta{prop} from \meta{label} and sets variable \meta{tl
%   var} with extracted value.  Ensure \cs{zref@extractdefault} is expanded
%   exactly twice, but no further to retrieve the proper value.  In case the
%   property is not found, set \meta{tl var} with \meta{default}.
%   \begin{syntax}
%     \cs{@@_extract_default:Nnnn} \Arg{tl var}
%     ~~\Arg{label} \Arg{prop} \Arg{default}
%   \end{syntax}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_extract_default:Nnnn #1#2#3#4
  {
    \exp_args:NNNo \exp_args:NNo \tl_set:Nn #1
      { \zref@extractdefault {#2} {#3} {#4} }
  }
\cs_generate_variant:Nn \@@_extract_default:Nnnn { NVnn , Nnvn }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_extract_unexp:nnn}
%   Extract property \meta{prop} from \meta{label}.  Ensure that, in the
%   context of an x expansion, \cs{zref@extractdefault} is expanded exactly
%   twice, but no further to retrieve the proper value.  Thus, this is meant
%   to be use in an x expansion context, not in other situations.  In case the
%   property is not found, leave \meta{default} in the stream.
%   \begin{syntax}
%     \cs{@@_extract_unexp:nnn}\Arg{label}\Arg{prop}\Arg{default}
%   \end{syntax}
%    \begin{macrocode}
\cs_new:Npn \@@_extract_unexp:nnn #1#2#3
  {
    \exp_args:NNo \exp_args:No
      \exp_not:n { \zref@extractdefault {#1} {#2} {#3} }
  }
\cs_generate_variant:Nn \@@_extract_unexp:nnn { Vnn , nvn , Vvn }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_extract:nnn}
%   An internal version for \cs{zref@extractdefault}.
%   \begin{syntax}
%     \cs{@@_extract:nnn}\Arg{label}\Arg{prop}\Arg{default}
%   \end{syntax}
%    \begin{macrocode}
\cs_new:Npn \@@_extract:nnn #1#2#3
  { \zref@extractdefault {#1} {#2} {#3} }
%    \end{macrocode}
% \end{macro}
%
%
% \subsection{Option infra}
%
% This section provides the functions in which the variables naming scheme of
% the package options is embodied, and some basic general functions to query
% these option variables.
%
% I had originally implemented the option handling of the package based on
% property lists, which are definitely very convenient.  But as the number of
% options grew, I started to get concerned about the performance implications.
% That there was a toll was noticeable, even when we could live with it, of
% course.  Indeed, at the time of writing, the typesetting of a reference
% queries about 24 different option values, most of them once per type-block,
% each of these queries can be potentially made in up to 5 option scope
% levels.  Considering the size of the built-in language files is running at
% the hundreds, the package does have a lot of work to do in querying option
% values alone, and thus it is best to smooth things in this area as much as
% possible.  This also gives me some peace of mind that the package will scale
% well in the long term.  For some interesting discussion about alternative
% methods and their performance implications, see
% \url{https://tex.stackexchange.com/q/147966}.  \contributor{Phelype Oleinik}
% also offered some insight on the matter at
% \url{https://tex.stackexchange.com/questions/629946/#comment1571118_629946}.
% The only real downside of this change is that we can no longer list the
% whole set of options in place at a given moment, which was useful for the
% purposes of regression testing, since we don't know what the whole set of
% active options is.
%
%
% \begin{macro}[EXP]{\@@_opt_varname_general:nn}
%   Defines, and leaves in the input stream, the csname of the variable used
%   to store the general \meta{option}.  The data type of the variable must be
%   specified (\texttt{tl}, \texttt{seq}, \texttt{bool}, etc.).
%   \begin{syntax}
%     \cs{@@_opt_varname_general:nn} \Arg{option} \Arg{data type}
%   \end{syntax}
%    \begin{macrocode}
\cs_new:Npn \@@_opt_varname_general:nn #1#2
  { l_@@_opt_general_ #1 _ #2 }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[EXP]{\@@_opt_varname_type:nnn}
%   Defines, and leaves in the input stream, the csname of the variable used
%   to store the type-specific \meta{option} for \meta{ref type}.
%   \begin{syntax}
%     \cs{@@_opt_varname_type:nnn} \Arg{ref type} \Arg{option} \Arg{data type}
%   \end{syntax}
%    \begin{macrocode}
\cs_new:Npn \@@_opt_varname_type:nnn #1#2#3
  { l_@@_opt_type_ #1 _ #2 _ #3 }
\cs_generate_variant:Nn \@@_opt_varname_type:nnn { enn , een }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[EXP]{\@@_opt_varname_language:nnn}
%   Defines, and leaves in the input stream, the csname of the variable used
%   to store the language \meta{option} for \meta{lang} (for general language
%   options, those set with \cs{zcDeclareLanguage}).  The
%   ``\texttt{lang_unknown}'' branch should be guarded against, such as we
%   normally should not get there, but this function \emph{must} return some
%   valid csname.  The random part is there so that, in the circumstance this
%   could not be avoided, we (hopefully) don't retrieve the value for an
%   ``unknown language'' inadvertently.
%   \begin{syntax}
%     \cs{@@_opt_varname_language:nnn} \Arg{lang} \Arg{option} \Arg{data type}
%   \end{syntax}
%    \begin{macrocode}
\cs_new:Npn \@@_opt_varname_language:nnn #1#2#3
  {
    \@@_language_if_declared:nTF {#1}
      {
        g_@@_opt_language_
        \tl_use:c { \@@_language_varname:n {#1} }
        _ #2 _ #3
      }
      { g_@@_opt_lang_unknown_ \int_rand:n { 1000000 } _ #3 }
  }
\cs_generate_variant:Nn \@@_opt_varname_language:nnn { enn }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[EXP]{\@@_opt_varname_lang_default:nnn}
%   Defines, and leaves in the input stream, the csname of the variable used
%   to store the language-specific default reference format \meta{option} for
%   \meta{lang}.
%   \begin{syntax}
%     \cs{@@_opt_varname_lang_default:nnn} \Arg{lang} \Arg{option} \Arg{data type}
%   \end{syntax}
%    \begin{macrocode}
\cs_new:Npn \@@_opt_varname_lang_default:nnn #1#2#3
  {
    \@@_language_if_declared:nTF {#1}
      {
        g_@@_opt_lang_
        \tl_use:c { \@@_language_varname:n {#1} }
        _default_ #2 _ #3
      }
      { g_@@_opt_lang_unknown_ \int_rand:n { 1000000 } _ #3 }
  }
\cs_generate_variant:Nn \@@_opt_varname_lang_default:nnn { enn }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[EXP]{\@@_opt_varname_lang_type:nnnn}
%   Defines, and leaves in the input stream, the csname of the variable used
%   to store the language- and type-specific reference format \meta{option}
%   for \meta{lang} and \meta{ref type}.
%   \begin{syntax}
%     \cs{@@_opt_varname_lang_type:nnnn} \Arg{lang} \Arg{ref type}
%     ~~\Arg{option} \Arg{data type}
%   \end{syntax}
%    \begin{macrocode}
\cs_new:Npn \@@_opt_varname_lang_type:nnnn #1#2#3#4
  {
    \@@_language_if_declared:nTF {#1}
      {
        g_@@_opt_lang_
        \tl_use:c { \@@_language_varname:n {#1} }
        _type_ #2 _ #3 _ #4
      }
      { g_@@_opt_lang_unknown_ \int_rand:n { 1000000 } _ #4 }
  }
\cs_generate_variant:Nn
  \@@_opt_varname_lang_type:nnnn { eenn , eeen }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[EXP]{\@@_opt_varname_fallback:nn}
%   Defines, and leaves in the input stream, the csname of the variable used
%   to store the fallback \meta{option}.
%   \begin{syntax}
%     \cs{@@_opt_varname_fallback:nn} \Arg{option} \Arg{data type}
%   \end{syntax}
%    \begin{macrocode}
\cs_new:Npn \@@_opt_varname_fallback:nn #1#2
  { c_@@_opt_fallback_ #1 _ #2 }
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}[EXP]{\@@_opt_var_set_bool:n}
%   The \LaTeX3 programming layer does not have the concept of a variable
%   \emph{existing} only locally, it also considers an ``error'' if an
%   assignment is made to a variable which was not previously declared, but
%   declaration is always global, which means that ``setting a local variable
%   at a local scope'', given these requirements, results in it existing, and
%   being empty, globally.  Therefore, we need an independent mechanism from
%   the mere existence of a variable to keep track of whether variables are
%   ``set'' or ``unset'', within the logic of the precedence rules for options
%   in different scopes.  \cs{@@_opt_var_set_bool:n} expands to the name of
%   the boolean variable used to track this state for \meta{option var}.  See
%   discussion with \contributor{Phelype Oleinik} at
%   \url{https://tex.stackexchange.com/questions/633341/#comment1579825_633347}
%   \begin{syntax}
%     \cs{@@_opt_var_set_bool:n} \Arg{option var}
%   \end{syntax}
%    \begin{macrocode}
\cs_new:Npn \@@_opt_var_set_bool:n #1
  { \cs_to_str:N #1 _is_set_bool }
%    \end{macrocode}
% \end{macro}
%
%
%
% \begin{macro}
%   {
%     \@@_opt_tl_set:Nn ,
%     \@@_opt_tl_clear:N ,
%     \@@_opt_tl_gset:Nn ,
%     \@@_opt_tl_gclear:N ,
%   }
%   \begin{syntax}
%     \cs{@@_opt_tl_set:N} \Arg{option tl} \Arg{value}
%     \cs{@@_opt_tl_clear:N} \Arg{option tl}
%     \cs{@@_opt_tl_gset:N} \Arg{option tl} \Arg{value}
%     \cs{@@_opt_tl_gclear:N} \Arg{option tl}
%   \end{syntax}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_opt_tl_set:Nn #1#2
  {
    \tl_if_exist:NF #1
      { \tl_new:N #1 }
    \tl_set:Nn #1 {#2}
    \bool_if_exist:cF { \@@_opt_var_set_bool:n {#1} }
      { \bool_new:c { \@@_opt_var_set_bool:n {#1} } }
    \bool_set_true:c { \@@_opt_var_set_bool:n {#1} }
  }
\cs_generate_variant:Nn \@@_opt_tl_set:Nn { cn }
\cs_new_protected:Npn \@@_opt_tl_clear:N #1
  {
    \tl_if_exist:NF #1
      { \tl_new:N #1 }
    \tl_clear:N #1
    \bool_if_exist:cF { \@@_opt_var_set_bool:n {#1} }
      { \bool_new:c { \@@_opt_var_set_bool:n {#1} } }
    \bool_set_true:c { \@@_opt_var_set_bool:n {#1} }
  }
\cs_generate_variant:Nn \@@_opt_tl_clear:N { c }
\cs_new_protected:Npn \@@_opt_tl_gset:Nn #1#2
  {
    \tl_if_exist:NF #1
      { \tl_new:N #1 }
    \tl_gset:Nn #1 {#2}
  }
\cs_generate_variant:Nn \@@_opt_tl_gset:Nn { cn }
\cs_new_protected:Npn \@@_opt_tl_gclear:N #1
  {
    \tl_if_exist:NF #1
      { \tl_new:N #1 }
    \tl_gclear:N #1
  }
\cs_generate_variant:Nn \@@_opt_tl_gclear:N { c }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_opt_tl_unset:N}
%   Unset \meta{option tl}.
%   \begin{syntax}
%     \cs{@@_opt_tl_unset:N} \Arg{option tl}
%   \end{syntax}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_opt_tl_unset:N #1
  {
    \tl_if_exist:NT #1
      {
        \tl_clear:N #1 % ?
        \bool_if_exist:cTF { \@@_opt_var_set_bool:n {#1} }
          { \bool_set_false:c { \@@_opt_var_set_bool:n {#1} } }
          { \bool_new:c { \@@_opt_var_set_bool:n {#1} } }
      }
  }
\cs_generate_variant:Nn \@@_opt_tl_unset:N { c }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[EXP,TF]{\@@_opt_tl_if_set:N}
%   This conditional \emph{defines} what means to be unset for a token list
%   option.  Note that the ``set bool'' not existing signals that the variable
%   \emph{is set}, that would be the case of all global option variables
%   (language-specific ones).  But this means care should be taken to always
%   define and set the ``set bool'' for local variables.
%   \begin{syntax}
%     \cs{@@_opt_tl_if_set:N(TF)} \Arg{option tl} \Arg{true} \Arg{false}
%   \end{syntax}
%    \begin{macrocode}
\prg_new_conditional:Npnn \@@_opt_tl_if_set:N #1 { F , TF }
  {
    \tl_if_exist:NTF #1
      {
        \bool_if_exist:cTF { \@@_opt_var_set_bool:n {#1} }
          {
            \bool_if:cTF { \@@_opt_var_set_bool:n {#1} }
              { \prg_return_true:  }
              { \prg_return_false: }
          }
          { \prg_return_true: }
      }
      { \prg_return_false: }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}
%   {
%     \@@_opt_tl_gset_if_new:Nn ,
%     \@@_opt_tl_gclear_if_new:N ,
%   }
%   \begin{syntax}
%     \cs{@@_opt_tl_gset_if_new:Nn} \Arg{option tl} \Arg{value}
%     \cs{@@_opt_tl_gclear_if_new:N} \Arg{option tl}
%   \end{syntax}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_opt_tl_gset_if_new:Nn #1#2
  {
    \@@_opt_tl_if_set:NF #1
      {
        \tl_if_exist:NF #1
          { \tl_new:N #1 }
        \tl_gset:Nn #1 {#2}
      }
  }
\cs_generate_variant:Nn \@@_opt_tl_gset_if_new:Nn { cn }
\cs_new_protected:Npn \@@_opt_tl_gclear_if_new:N #1
  {
    \@@_opt_tl_if_set:NF #1
      {
        \tl_if_exist:NF #1
          { \tl_new:N #1 }
        \tl_gclear:N #1
      }
  }
\cs_generate_variant:Nn \@@_opt_tl_gclear_if_new:N { c }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[TF]{\@@_opt_tl_get:NN}
%   \begin{syntax}
%     \cs{@@_opt_tl_get:NN(TF)} \Arg{option tl to get} \Arg{tl var to set}
%     ~~\Arg{true} \Arg{false}
%   \end{syntax}
%    \begin{macrocode}
\prg_new_protected_conditional:Npnn \@@_opt_tl_get:NN #1#2 { F }
  {
    \@@_opt_tl_if_set:NTF #1
      {
        \tl_set_eq:NN #2 #1
        \prg_return_true:
      }
      { \prg_return_false: }
  }
\prg_generate_conditional_variant:Nnn
  \@@_opt_tl_get:NN { cN } { F }
%    \end{macrocode}
% \end{macro}
%
%
%
% \begin{macro}
%   {
%     \@@_opt_seq_set_clist_split:Nn ,
%     \@@_opt_seq_gset_clist_split:Nn ,
%     \@@_opt_seq_set_eq:NN ,
%     \@@_opt_seq_gset_eq:NN ,
%   }
%   \begin{syntax}
%     \cs{@@_opt_seq_set_clist_split:Nn} \Arg{option seq} \Arg{value}
%     \cs{@@_opt_seq_gset_clist_split:Nn} \Arg{option seq} \Arg{value}
%     \cs{@@_opt_seq_set_eq:NN} \Arg{option seq} \Arg{seq var}
%     \cs{@@_opt_seq_gset_eq:NN} \Arg{option seq} \Arg{seq var}
%   \end{syntax}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_opt_seq_set_clist_split:Nn #1#2
  { \seq_set_split:Nnn #1 { , } {#2} }
\cs_new_protected:Npn \@@_opt_seq_gset_clist_split:Nn #1#2
  { \seq_gset_split:Nnn #1 { , } {#2} }
\cs_new_protected:Npn \@@_opt_seq_set_eq:NN #1#2
  {
    \seq_if_exist:NF #1
      { \seq_new:N #1 }
    \seq_set_eq:NN #1 #2
    \bool_if_exist:cF { \@@_opt_var_set_bool:n {#1} }
      { \bool_new:c { \@@_opt_var_set_bool:n {#1} } }
    \bool_set_true:c { \@@_opt_var_set_bool:n {#1} }
  }
\cs_generate_variant:Nn \@@_opt_seq_set_eq:NN { cN }
\cs_new_protected:Npn \@@_opt_seq_gset_eq:NN #1#2
  {
    \seq_if_exist:NF #1
      { \seq_new:N #1 }
    \seq_gset_eq:NN #1 #2
  }
\cs_generate_variant:Nn \@@_opt_seq_gset_eq:NN { cN }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_opt_seq_unset:N}
%   Unset \meta{option seq}.
%   \begin{syntax}
%     \cs{@@_opt_seq_unset:N} \Arg{option seq}
%   \end{syntax}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_opt_seq_unset:N #1
  {
    \seq_if_exist:NT #1
      {
        \seq_clear:N #1 % ?
        \bool_if_exist:cTF { \@@_opt_var_set_bool:n {#1} }
          { \bool_set_false:c { \@@_opt_var_set_bool:n {#1} } }
          { \bool_new:c { \@@_opt_var_set_bool:n {#1} } }
      }
  }
\cs_generate_variant:Nn \@@_opt_seq_unset:N { c }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[EXP,TF]{\@@_opt_seq_if_set:N}
%   This conditional \emph{defines} what means to be unset for a sequence
%   option.
%   \begin{syntax}
%     \cs{@@_opt_seq_if_set:N(TF)} \Arg{option seq} \Arg{true} \Arg{false}
%   \end{syntax}
%    \begin{macrocode}
\prg_new_conditional:Npnn \@@_opt_seq_if_set:N #1 { F , TF }
  {
    \seq_if_exist:NTF #1
      {
        \bool_if_exist:cTF { \@@_opt_var_set_bool:n {#1} }
          {
            \bool_if:cTF { \@@_opt_var_set_bool:n {#1} }
              { \prg_return_true:  }
              { \prg_return_false: }
          }
          { \prg_return_true: }
      }
      { \prg_return_false: }
  }
\prg_generate_conditional_variant:Nnn
  \@@_opt_seq_if_set:N { c } { F , TF }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[TF]{\@@_opt_seq_get:NN}
%   \begin{syntax}
%     \cs{@@_opt_seq_get:NN(TF)} \Arg{option seq to get} \Arg{seq var to set}
%     ~~\Arg{true} \Arg{false}
%   \end{syntax}
%    \begin{macrocode}
\prg_new_protected_conditional:Npnn \@@_opt_seq_get:NN #1#2 { F }
  {
    \@@_opt_seq_if_set:NTF #1
      {
        \seq_set_eq:NN #2 #1
        \prg_return_true:
      }
      { \prg_return_false: }
  }
\prg_generate_conditional_variant:Nnn
  \@@_opt_seq_get:NN { cN } { F }
%    \end{macrocode}
% \end{macro}
%
%
%
% \begin{macro}{\@@_opt_bool_unset:N}
%   Unset \meta{option bool}.
%   \begin{syntax}
%     \cs{@@_opt_bool_unset:N} \Arg{option bool}
%   \end{syntax}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_opt_bool_unset:N #1
  {
    \bool_if_exist:NT #1
      {
        % \bool_set_false:N #1 % ?
        \bool_if_exist:cTF { \@@_opt_var_set_bool:n {#1} }
          { \bool_set_false:c { \@@_opt_var_set_bool:n {#1} } }
          { \bool_new:c { \@@_opt_var_set_bool:n {#1} } }
      }
  }
\cs_generate_variant:Nn \@@_opt_bool_unset:N { c }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[EXP,TF]{\@@_opt_bool_if_set:N}
%   This conditional \emph{defines} what means to be unset for a boolean
%   option.
%   \begin{syntax}
%     \cs{@@_opt_bool_if_set:N(TF)} \Arg{option bool} \Arg{true} \Arg{false}
%   \end{syntax}
%    \begin{macrocode}
\prg_new_conditional:Npnn \@@_opt_bool_if_set:N #1 { F , TF }
  {
    \bool_if_exist:NTF #1
      {
        \bool_if_exist:cTF { \@@_opt_var_set_bool:n {#1} }
          {
            \bool_if:cTF { \@@_opt_var_set_bool:n {#1} }
              { \prg_return_true:  }
              { \prg_return_false: }
          }
          { \prg_return_true: }
      }
      { \prg_return_false: }
  }
\prg_generate_conditional_variant:Nnn
  \@@_opt_bool_if_set:N { c } { F , TF }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}
%   {
%     \@@_opt_bool_set_true:N ,
%     \@@_opt_bool_set_false:N ,
%     \@@_opt_bool_gset_true:N ,
%     \@@_opt_bool_gset_false:N ,
%   }
%   \begin{syntax}
%     \cs{@@_opt_bool_set_true:N} \Arg{option bool}
%     \cs{@@_opt_bool_set_false:N} \Arg{option bool}
%     \cs{@@_opt_bool_gset_true:N} \Arg{option bool}
%     \cs{@@_opt_bool_gset_false:N} \Arg{option bool}
%   \end{syntax}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_opt_bool_set_true:N #1
  {
    \bool_if_exist:NF #1
      { \bool_new:N #1 }
    \bool_set_true:N #1
    \bool_if_exist:cF { \@@_opt_var_set_bool:n {#1} }
      { \bool_new:c { \@@_opt_var_set_bool:n {#1} } }
    \bool_set_true:c { \@@_opt_var_set_bool:n {#1} }
  }
\cs_generate_variant:Nn \@@_opt_bool_set_true:N { c }
\cs_new_protected:Npn \@@_opt_bool_set_false:N #1
  {
    \bool_if_exist:NF #1
      { \bool_new:N #1 }
    \bool_set_false:N #1
    \bool_if_exist:cF { \@@_opt_var_set_bool:n {#1} }
      { \bool_new:c { \@@_opt_var_set_bool:n {#1} } }
    \bool_set_true:c { \@@_opt_var_set_bool:n {#1} }
  }
\cs_generate_variant:Nn \@@_opt_bool_set_false:N { c }
\cs_new_protected:Npn \@@_opt_bool_gset_true:N #1
  {
    \bool_if_exist:NF #1
      { \bool_new:N #1 }
    \bool_gset_true:N #1
  }
\cs_generate_variant:Nn \@@_opt_bool_gset_true:N { c }
\cs_new_protected:Npn \@@_opt_bool_gset_false:N #1
  {
    \bool_if_exist:NF #1
      { \bool_new:N #1 }
    \bool_gset_false:N #1
  }
\cs_generate_variant:Nn \@@_opt_bool_gset_false:N { c }
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}[TF]{\@@_opt_bool_get:NN}
%   \begin{syntax}
%     \cs{@@_opt_bool_get:NN(TF)} \Arg{option bool to get} \Arg{bool var to set}
%     ~~\Arg{true} \Arg{false}
%   \end{syntax}
%    \begin{macrocode}
\prg_new_protected_conditional:Npnn \@@_opt_bool_get:NN #1#2 { F }
  {
    \@@_opt_bool_if_set:NTF #1
      {
        \bool_set_eq:NN #2 #1
        \prg_return_true:
      }
      { \prg_return_false: }
  }
\prg_generate_conditional_variant:Nnn
  \@@_opt_bool_get:NN { cN } { F }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[EXP,TF]{\@@_opt_bool_if:N}
%   \begin{syntax}
%     \cs{@@_opt_bool_if:N(TF)} \Arg{option bool} \Arg{true} \Arg{false}
%   \end{syntax}
%    \begin{macrocode}
\prg_new_conditional:Npnn \@@_opt_bool_if:N #1 { T , F , TF }
  {
    \@@_opt_bool_if_set:NTF #1
      { \bool_if:NTF #1 { \prg_return_true: } { \prg_return_false: } }
      { \prg_return_false: }
  }
\prg_generate_conditional_variant:Nnn
  \@@_opt_bool_if:N { c } { T , F , TF }
%    \end{macrocode}
% \end{macro}
%
%
%
% \subsection{Reference format}
%
% For a general discussion on the precedence rules for reference format
% options, see Section ``Reference format'' in the User manual.  Internally,
% these precedence rules are handled / enforced in \cs{@@_get_rf_opt_tl:nnnN},
% \cs{@@_get_rf_opt_seq:nnnN}, \cs{@@_get_rf_opt_bool:nnnnN}, and
% \cs{@@_type_name_setup:} which are the basic functions to retrieve proper
% values for reference format settings.
%
% The fact that we have multiple scopes to set reference format options has
% some implications for how we handle these options, and for the resulting UI.
% Since there is a clear precedence rule between the different levels, setting
% an option at a high priority level shadows everything below it.  Hence, it
% may be relevant to be able to ``unset'' these options too, so as to be able
% go back to the lower precedence level of the language-specific options at
% any given point.  However, since many of these options are token lists, or
% clists, for which ``empty'' is a legitimate value, we cannot rely on
% emptiness to distinguish that particular intention.  How to deal with it,
% depends on the kind of option (its data type, to be precise).  For token
% lists and clists/sequences, we leverage the distinction of an ``empty valued
% key'' (\texttt{key=} or \texttt{key=\{\}}) from a ``key with no value''
% (\texttt{key}).  This distinction is captured internally by the lower-level
% key parsing, but must be made explicit in \cs{keys_define:nn} by means of
% the \texttt{.default:o} property of the key.  For the technique, by
% \contributor{Jonathan P.\ Spratte}, aka `Skillmon', and some discussion
% about it, including further insights by \contributor{Phelype Oleinik}, see
% \url{https://tex.stackexchange.com/q/614690} and
% \url{https://github.com/latex3/latex3/pull/988}.  However, Joseph Wright
% seems to particularly dislike this use and the general idea of a ``key with
% no value'' being somehow meaningful for \pkg{l3keys} (e.g. his comments on
% the previous question, and
% \url{https://tex.stackexchange.com/q/632157/#comment1576404_632157}), which
% does make it somewhat risky to rely on this.  For booleans, the situation is
% different, since they cannot meaningfully receive an empty value and the
% ``key with no value'' is a handy and expected shorthand for
% \texttt{key=true}.  Therefore, for reference format option booleans, we use
% a third value ``\texttt{unset}'' for this purpose.  And similarly for
% ``choice'' options.
%
% However, ``unsetting'' options is only supported at the general and
% reference type levels, that is, at \cs{zcsetup}, at \cs{zcref}, and at
% \cs{zcRefTypeSetup}.  For language-specific options -- in the language files
% or at \cs{zcLanguageSetup} -- there is no unsetting, an option which has
% been set can there only be changed to another value.  This for two reasons.
% First, these are low precedence levels, so it is less meaningful to be able
% to unset these options.  Second, these settings can only be done in the
% preamble (or the package itself).  They are meant to be global.  So, do it
% once, do it right, and if you need to locally change something along the
% document, use a higher precedence level.
%
%
% \begin{macro}
%   {
%     \l_@@_setup_type_tl ,
%     \l_@@_setup_language_tl ,
%     \l_@@_lang_decl_case_tl ,
%     \l_@@_lang_declension_seq ,
%     \l_@@_lang_gender_seq ,
%   }
%   Store ``current'' type, language, and declension cases in different places
%   for type-specific and language-specific options handling, notably in
%   \cs{@@_provide_langfile:n}, \cs{zcRefTypeSetup}, and
%   \cs{zcLanguageSetup}, but also for language specific options retrieval.
%    \begin{macrocode}
\tl_new:N \l_@@_setup_type_tl
\tl_new:N \l_@@_setup_language_tl
\tl_new:N \l_@@_lang_decl_case_tl
\seq_new:N \l_@@_lang_declension_seq
\seq_new:N \l_@@_lang_gender_seq
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}
%   {
%     \g_@@_rf_opts_tl_not_type_specific_seq ,
%     \g_@@_rf_opts_tl_maybe_type_specific_seq ,
%     \g_@@_rf_opts_seq_refbounds_seq ,
%     \g_@@_rf_opts_bool_maybe_type_specific_seq ,
%     \g_@@_rf_opts_tl_type_names_seq ,
%     \g_@@_rf_opts_tl_typesetup_seq ,
%     \g_@@_rf_opts_tl_reference_seq ,
%   }
%     Lists of reference format options in ``categories''.  Since these
%     options are set in different scopes, and at different places, storing
%     the actual lists in centralized variables makes the job not only easier
%     later on, but also keeps things consistent.  These variables are
%     \emph{constants}, but I don't seem to be able to find a way to
%     concatenate two constants into a third one without triggering \LaTeX3
%     debug error ``Inconsistent local/global assignment''.  And repeating
%     things in a new \cs{seq_const_from_clist:Nn} defeats the purpose of
%     these variables.
%    \begin{macrocode}
\seq_new:N \g_@@_rf_opts_tl_not_type_specific_seq
\seq_gset_from_clist:Nn
  \g_@@_rf_opts_tl_not_type_specific_seq
  {
    tpairsep ,
    tlistsep ,
    tlastsep ,
    notesep ,
  }
\seq_new:N \g_@@_rf_opts_tl_maybe_type_specific_seq
\seq_gset_from_clist:Nn
  \g_@@_rf_opts_tl_maybe_type_specific_seq
  {
    namesep ,
    pairsep ,
    listsep ,
    lastsep ,
    rangesep ,
    namefont ,
    reffont ,
  }
\seq_new:N \g_@@_rf_opts_seq_refbounds_seq
\seq_gset_from_clist:Nn
  \g_@@_rf_opts_seq_refbounds_seq
  {
    refbounds-first ,
    refbounds-first-sg ,
    refbounds-first-pb ,
    refbounds-first-rb ,
    refbounds-mid ,
    refbounds-mid-rb ,
    refbounds-mid-re ,
    refbounds-last ,
    refbounds-last-pe ,
    refbounds-last-re ,
  }
\seq_new:N \g_@@_rf_opts_bool_maybe_type_specific_seq
\seq_gset_from_clist:Nn
  \g_@@_rf_opts_bool_maybe_type_specific_seq
  {
    cap ,
    abbrev ,
    rangetopair ,
  }
%    \end{macrocode}
% Only ``type names'' are ``necessarily type-specific'', which makes them
% somewhat special on the retrieval side of things.  In short, they don't have
% their values queried by \cs{@@_get_rf_opt_tl:nnnN}, but by
% \cs{@@_type_name_setup:}.
%    \begin{macrocode}
\seq_new:N \g_@@_rf_opts_tl_type_names_seq
\seq_gset_from_clist:Nn
  \g_@@_rf_opts_tl_type_names_seq
  {
    Name-sg ,
    name-sg ,
    Name-pl ,
    name-pl ,
    Name-sg-ab ,
    name-sg-ab ,
    Name-pl-ab ,
    name-pl-ab ,
  }
%    \end{macrocode}
% And, finally, some combined groups of the above variables, for convenience.
%    \begin{macrocode}
\seq_new:N \g_@@_rf_opts_tl_typesetup_seq
\seq_gconcat:NNN \g_@@_rf_opts_tl_typesetup_seq
  \g_@@_rf_opts_tl_maybe_type_specific_seq
  \g_@@_rf_opts_tl_type_names_seq
\seq_new:N \g_@@_rf_opts_tl_reference_seq
\seq_gconcat:NNN \g_@@_rf_opts_tl_reference_seq
  \g_@@_rf_opts_tl_not_type_specific_seq
  \g_@@_rf_opts_tl_maybe_type_specific_seq
%    \end{macrocode}
% \end{macro}
%
%
% We set here also the ``derived'' \texttt{refbounds} options, which are
% (almost) the same for every option scope.
%
%    \begin{macrocode}
\clist_map_inline:nn
  {
    reference ,
    typesetup ,
    langsetup ,
    langfile ,
  }
  {
    \keys_define:nn { zref-clever/ #1 }
      {
        +refbounds-first .meta:n =
          {
            refbounds-first = {##1} ,
            refbounds-first-sg = {##1} ,
            refbounds-first-pb = {##1} ,
            refbounds-first-rb = {##1} ,
          } ,
        +refbounds-mid .meta:n =
          {
            refbounds-mid = {##1} ,
            refbounds-mid-rb = {##1} ,
            refbounds-mid-re = {##1} ,
          } ,
        +refbounds-last .meta:n =
          {
            refbounds-last = {##1} ,
            refbounds-last-pe = {##1} ,
            refbounds-last-re = {##1} ,
          } ,
        +refbounds-rb .meta:n =
          {
            refbounds-first-rb = {##1} ,
            refbounds-mid-rb = {##1} ,
          } ,
        +refbounds-re .meta:n =
          {
            refbounds-mid-re = {##1} ,
            refbounds-last-re = {##1} ,
          } ,
        +refbounds .meta:n =
          {
            +refbounds-first = {##1} ,
            +refbounds-mid = {##1} ,
            +refbounds-last = {##1} ,
          } ,
        refbounds .meta:n = { +refbounds = {##1} } ,
      }
  }
\clist_map_inline:nn
  {
    reference ,
    typesetup ,
  }
  {
    \keys_define:nn { zref-clever/ #1 }
      {
        +refbounds-first .default:o = \c_novalue_tl ,
        +refbounds-mid .default:o = \c_novalue_tl ,
        +refbounds-last .default:o = \c_novalue_tl ,
        +refbounds-rb .default:o = \c_novalue_tl ,
        +refbounds-re .default:o = \c_novalue_tl ,
        +refbounds .default:o = \c_novalue_tl ,
        refbounds .default:o = \c_novalue_tl ,
      }
  }
\clist_map_inline:nn
  {
    langsetup ,
    langfile ,
  }
  {
    \keys_define:nn { zref-clever/ #1 }
      {
        +refbounds-first .value_required:n = true ,
        +refbounds-mid .value_required:n = true ,
        +refbounds-last .value_required:n = true ,
        +refbounds-rb .value_required:n = true ,
        +refbounds-re .value_required:n = true ,
        +refbounds .value_required:n = true ,
        refbounds .value_required:n = true ,
      }
  }
%    \end{macrocode}
%
%
% \subsection{Languages}
%
%
% \cs{l_@@_current_language_tl} is an internal alias for \pkg{babel}'s
% \cs{languagename} or \pkg{polyglossia}'s \cs{mainbabelname} and, if none of
% them is loaded, we set it to \texttt{english}.  \cs{l_@@_main_language_tl}
% is an internal alias for \pkg{babel}'s \cs{bbl@main@language} or for
% \pkg{polyglossia}'s \cs{mainbabelname}, as the case may be.  Note that for
% \pkg{polyglossia} we get \pkg{babel}'s language names, so that we only need
% to handle those internally.  \cs{l_@@_ref_language_tl} is the internal
% variable which stores the language in which the reference is to be made.
%
%    \begin{macrocode}
\tl_new:N \l_@@_ref_language_tl
\tl_new:N \l_@@_current_language_tl
\tl_new:N \l_@@_main_language_tl
%    \end{macrocode}
%
% \begin{macro}{\l_zrefclever_ref_language_tl}
%   A public version of \cs{l_@@_ref_language_tl} for use in \pkg{zref-vario}.
%    \begin{macrocode}
\tl_new:N \l_zrefclever_ref_language_tl
\tl_set:Nn \l_zrefclever_ref_language_tl { \l_@@_ref_language_tl }
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}[EXP]{\@@_language_varname:n}
%   Defines, and leaves in the input stream, the csname of the variable used
%   to store the \meta{base language} (as the value of this variable) for
%   a \meta{language} declared for \pkg{zref-clever}.
%   \begin{syntax}
%     \cs{@@_language_varname:n} \Arg{language}
%   \end{syntax}
%    \begin{macrocode}
\cs_new:Npn \@@_language_varname:n #1
  { g_@@_declared_language_ #1 _tl }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[EXP]{\zrefclever_language_varname:n}
%   A public version of \cs{@@_language_varname:n} for use in
%   \pkg{zref-vario}.
%    \begin{macrocode}
\cs_set_eq:NN \zrefclever_language_varname:n
  \@@_language_varname:n
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[EXP,TF]{\@@_language_if_declared:n}
%   A language is considered to be declared for \pkg{zref-clever} if it passes
%   this conditional, which requires that a variable with
%   \cs{@@_language_varname:n}\texttt{\{\meta{language}\}} exists.
%   \begin{syntax}
%     \cs{@@_language_if_declared:n(TF)} \Arg{language}
%   \end{syntax}
%    \begin{macrocode}
\prg_new_conditional:Npnn \@@_language_if_declared:n #1 { T , F , TF }
  {
    \tl_if_exist:cTF { \@@_language_varname:n {#1} }
     { \prg_return_true:  }
     { \prg_return_false: }
  }
\prg_generate_conditional_variant:Nnn
  \@@_language_if_declared:n { x } { T , F , TF }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[EXP,TF]{\zrefclever_language_if_declared:n}
%   A public version of \cs{@@_language_if_declared:n} for use in
%   \pkg{zref-vario}.
%    \begin{macrocode}
\prg_set_eq_conditional:NNn \zrefclever_language_if_declared:n
  \@@_language_if_declared:n { TF }
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}[int]{\zcDeclareLanguage}
% Declare a new language for use with \pkg{zref-clever}.  \meta{language} is
% taken to be both the ``language name'' and the ``base language name''.  A
% ``base language'' (loose concept here, meaning just ``the name we gave for
% the language file in that particular language'') is just like any other one,
% the only difference is that the ``language name'' happens to be the same as
% the ``base language name'', in other words, it is an ``alias to itself''.
% \oarg{options} receive a \texttt{k=v} set of options, with three valid
% options.  The first, \opt{declension}, takes the noun declension cases
% prefixes for \meta{language} as a comma separated list, whose first element
% is taken to be the default case.  The second, \opt{gender}, receives the
% genders for \meta{language} as comma separated list.  The third,
% \opt{allcaps}, is a boolean, and indicates that for \meta{language} all
% nouns must be capitalized for grammatical reasons, in which case, the
% \opt{cap} option is disregarded for \meta{language}.  If \meta{language} is
% already known, just warn.  This implies a particular restriction regarding
% \oarg{options}, namely that these options, when defined by the package,
% cannot be redefined by the user.  This is deliberate, otherwise the built-in
% language files would become much too sensitive to this particular user
% input, and unnecessarily so.  \cs{zcDeclareLanguage} is preamble only.
%   \begin{syntax}
%     \cs{zcDeclareLanguage} \oarg{options} \marg{language}
%   \end{syntax}
%    \begin{macrocode}
\NewDocumentCommand \zcDeclareLanguage { O { } m }
  {
    \group_begin:
    \tl_if_empty:nF {#2}
      {
        \@@_language_if_declared:nTF {#2}
          { \msg_warning:nnn { zref-clever } { language-declared } {#2} }
          {
            \tl_new:c { \@@_language_varname:n {#2} }
            \tl_gset:cn { \@@_language_varname:n {#2} } {#2}
            \tl_set:Nn \l_@@_setup_language_tl {#2}
            \keys_set:nn { zref-clever/declarelang } {#1}
          }
      }
    \group_end:
  }
\@onlypreamble \zcDeclareLanguage
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}[int]{\zcDeclareLanguageAlias}
%   Declare \meta{language alias} to be an alias of \meta{aliased language}
%   (or ``base language'').  \meta{aliased language} must be already known to
%   \pkg{zref-clever}.
%   \cs{zcDeclareLanguageAlias} is preamble only.
%   \begin{syntax}
%     \cs{zcDeclareLanguageAlias} \marg{language alias} \marg{aliased language}
%   \end{syntax}
%    \begin{macrocode}
\NewDocumentCommand \zcDeclareLanguageAlias { m m }
  {
    \tl_if_empty:nF {#1}
      {
        \@@_language_if_declared:nTF {#2}
          {
            \tl_new:c { \@@_language_varname:n {#1} }
            \tl_gset:cx { \@@_language_varname:n {#1} }
              { \tl_use:c { \@@_language_varname:n {#2} } }
          }
          { \msg_warning:nnn { zref-clever } { unknown-language-alias } {#2} }
      }
  }
\@onlypreamble \zcDeclareLanguageAlias
%    \end{macrocode}
% \end{macro}
%
%
%
%    \begin{macrocode}
\keys_define:nn { zref-clever/declarelang }
  {
    declension .code:n =
      {
        \seq_new:c
          {
            \@@_opt_varname_language:enn
              { \l_@@_setup_language_tl } { declension } { seq }
          }
        \seq_gset_from_clist:cn
          {
            \@@_opt_varname_language:enn
              { \l_@@_setup_language_tl } { declension } { seq }
          }
          {#1}
      } ,
    declension .value_required:n = true ,
    gender .code:n =
      {
        \seq_new:c
          {
            \@@_opt_varname_language:enn
              { \l_@@_setup_language_tl } { gender } { seq }
          }
        \seq_gset_from_clist:cn
          {
            \@@_opt_varname_language:enn
              { \l_@@_setup_language_tl } { gender } { seq }
          }
          {#1}
      } ,
    gender .value_required:n = true ,
    allcaps .choices:nn =
      { true , false }
      {
        \bool_new:c
          {
            \@@_opt_varname_language:enn
              { \l_@@_setup_language_tl } { allcaps } { bool }
          }
        \use:c { bool_gset_ \l_keys_choice_tl :c }
          {
            \@@_opt_varname_language:enn
              { \l_@@_setup_language_tl } { allcaps } { bool }
          }
      } ,
    allcaps .default:n = true ,
  }
%    \end{macrocode}
%
%
%
% \begin{macro}{\@@_process_language_settings:}
%   Auxiliary function for \cs{@@_zcref:nnn}, responsible for processing
%   language related settings.  It is necessary to separate them from the
%   reference options machinery for two reasons.  First, because their
%   behavior is language dependent, but the language itself can also be set as
%   an option (\opt{lang}, value stored in \cs{l_@@_ref_language_tl}).
%   Second, some of its tasks must be done regardless of any option being
%   given (e.g. the default declension case, the \opt{allcaps} option).
%   Hence, we must validate the language settings after the reference options
%   have been set.  It is expected to be called right (or soon) after
%   \cs{keys_set:nn} in \cs{@@_zcref:nnn}, where current values for
%   \cs{l_@@_ref_language_tl} and \cs{l_@@_ref_decl_case_tl} are in place.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_process_language_settings:
  {
    \@@_language_if_declared:xTF
      { \l_@@_ref_language_tl }
      {
%    \end{macrocode}
% Validate the declension case (\opt{d}) option against the declared cases for
% the reference language.  If the user value for the latter does not match the
% declension cases declared for the former, the function sets an appropriate
% value for \cs{l_@@_ref_decl_case_tl}, either using the default case, or
% clearing the variable, depending on the language setup.  And also issues a
% warning about it.
%    \begin{macrocode}
        \@@_opt_seq_get:cNF
          {
            \@@_opt_varname_language:enn
              { \l_@@_ref_language_tl } { declension } { seq }
          }
          \l_@@_lang_declension_seq
          { \seq_clear:N \l_@@_lang_declension_seq }
        \seq_if_empty:NTF \l_@@_lang_declension_seq
          {
            \tl_if_empty:NF \l_@@_ref_decl_case_tl
              {
                \msg_warning:nnxx { zref-clever }
                  { language-no-decl-ref }
                  { \l_@@_ref_language_tl }
                  { \l_@@_ref_decl_case_tl }
                \tl_clear:N \l_@@_ref_decl_case_tl
              }
          }
          {
            \tl_if_empty:NTF \l_@@_ref_decl_case_tl
              {
                \seq_get_left:NN \l_@@_lang_declension_seq
                  \l_@@_ref_decl_case_tl
              }
              {
                \seq_if_in:NVF \l_@@_lang_declension_seq
                  \l_@@_ref_decl_case_tl
                  {
                    \msg_warning:nnxx { zref-clever }
                      { unknown-decl-case }
                      { \l_@@_ref_decl_case_tl }
                      { \l_@@_ref_language_tl }
                    \seq_get_left:NN \l_@@_lang_declension_seq
                      \l_@@_ref_decl_case_tl
                  }
              }
          }
%    \end{macrocode}
% Validate the gender (\opt{g}) option against the declared genders for the
% reference language.  If the user value for the latter does not match the
% genders declared for the former, clear \cs{l_@@_ref_gender_tl} and warn.
%    \begin{macrocode}
        \@@_opt_seq_get:cNF
          {
            \@@_opt_varname_language:enn
              { \l_@@_ref_language_tl } { gender } { seq }
          }
          \l_@@_lang_gender_seq
          { \seq_clear:N \l_@@_lang_gender_seq }
        \seq_if_empty:NTF \l_@@_lang_gender_seq
          {
            \tl_if_empty:NF \l_@@_ref_gender_tl
              {
                \msg_warning:nnxxx { zref-clever }
                  { language-no-gender }
                  { \l_@@_ref_language_tl }
                  { g }
                  { \l_@@_ref_gender_tl }
                \tl_clear:N \l_@@_ref_gender_tl
              }
          }
          {
            \tl_if_empty:NF \l_@@_ref_gender_tl
              {
                \seq_if_in:NVF \l_@@_lang_gender_seq
                  \l_@@_ref_gender_tl
                  {
                    \msg_warning:nnxx { zref-clever }
                      { gender-not-declared }
                      { \l_@@_ref_language_tl }
                      { \l_@@_ref_gender_tl }
                    \tl_clear:N \l_@@_ref_gender_tl
                  }
              }
          }
%    \end{macrocode}
% Ensure the general \opt{cap} is set to \texttt{true} when the language was
% declared with \opt{allcaps} option.
%    \begin{macrocode}
        \@@_opt_bool_if:cT
          {
            \@@_opt_varname_language:enn
              { \l_@@_ref_language_tl } { allcaps } { bool }
          }
          { \keys_set:nn { zref-clever/reference } { cap = true } }
      }
      {
%    \end{macrocode}
% If the language itself is not declared, we still have to issue declension
% and gender warnings, if \opt{d} or \opt{g} options were used.
%    \begin{macrocode}
        \tl_if_empty:NF \l_@@_ref_decl_case_tl
          {
            \msg_warning:nnxx { zref-clever } { unknown-language-decl }
              { \l_@@_ref_decl_case_tl }
              { \l_@@_ref_language_tl }
            \tl_clear:N \l_@@_ref_decl_case_tl
          }
        \tl_if_empty:NF \l_@@_ref_gender_tl
          {
            \msg_warning:nnxxx { zref-clever }
              { language-no-gender }
              { \l_@@_ref_language_tl }
              { g }
              { \l_@@_ref_gender_tl }
            \tl_clear:N \l_@@_ref_gender_tl
          }
      }
  }
%    \end{macrocode}
% \end{macro}
%
%
%
% \subsection{Language files}
%
% Contrary to general options and type options, which are always \emph{local},
% language-specific settings are always \emph{global}.  Hence, the loading of
% built-in language files, as well as settings done with \cs{zcLanguageSetup},
% should set the relevant variables globally.
%
% The built-in language files and their related infrastructure are designed to
% perform ``on the fly'' loading of the language files, ``lazily'' as needed.
% Much like \pkg{babel} does for languages not declared in the preamble, but
% used in the document.  This offers some convenience, of course, and that's
% one reason to do it.  But it also has the purpose of parsimony, of ``loading
% the least possible''.  Therefore, we load at \texttt{begindocument} one
% single language (see \zcref[ref=title, noname]{sec:lang-option}), as
% specified by the user in the preamble with the \opt{lang} option or, failing
% any specification, the current language of the document, which is the
% default.  Anything else is lazily loaded, on the fly, along the document.
%
% This design decision has also implications to the \emph{form} the language
% files assumed.  As far as my somewhat impressionistic sampling goes,
% dictionary or localization files of the most common packages in this area of
% functionality, are usually a set of commands which perform the relevant
% definitions and assignments in the preamble or at \texttt{begindocument}.
% This includes \pkg{translator}, \pkg{translations}, but also \pkg{babel}'s
% \file{.ldf} files, and \pkg{biblatex}'s \file{.lbx} files.  I'm not really
% well acquainted with this machinery, but as far as I grasp, they all rely on
% some variation of \cs{ProvidesFile} and \cs{input}.  And they can be safely
% \cs{input} without generating spurious content, because they rely on being
% loaded before the document has actually started.  As far as I can tell,
% \pkg{babel}'s ``on the fly'' functionality is not based on the \file{.ldf}
% files, but on the \file{.ini} files, and on \cs{babelprovide}.  And the
% \file{.ini} files are not in this form, but actually resemble
% ``configuration files'' of sorts, which means they are read and processed
% somehow else than with just \cs{input}.  So we do the more or less the same
% here.  It seems a reasonable way to ensure we can load language files on the
% fly robustly mid-document, without getting paranoid with the last bit of
% white-space in them, and without introducing any undue content on the stream
% when we cannot afford to do it.  Hence, \pkg{zref-clever}'s built-in
% language files are a set of \emph{key-value options} which are read from the
% file, and fed to \texttt{\cs{keys_set:nn}\{zref-clever/langfile\}} by
% \cs{@@_provide_langfile:n}.  And they use the same syntax and options as
% \cs{zcLanguageSetup} does.  The language file itself is read with
% \cs{ExplSyntaxOn} with the usual implications for white-space and catcodes.
%
% \cs{@@_provide_langfile:n} is only meant to load the built-in language
% files.  For languages declared by the user, or for any settings to a known
% language made with \cs{zcLanguageSetup}, values are populated directly to a
% corresponding variables.  Hence, there is no need to ``load'' anything in
% this case: definitions and assignments made by the user are performed
% immediately.
%
%
% \begin{macro}{\g_@@_loaded_langfiles_seq}
%   Used to keep track of whether a language file has already been loaded or
%   not.
%    \begin{macrocode}
\seq_new:N \g_@@_loaded_langfiles_seq
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}{\@@_provide_langfile:n}
%   Load language file for known \meta{language} if it is available and if it
%   has not already been loaded.
%   \begin{syntax}
%     \cs{@@_provide_langfile:n} \Arg{language}
%   \end{syntax}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_provide_langfile:n #1
  {
    \group_begin:
    \@bsphack
    \@@_language_if_declared:nT {#1}
      {
        \seq_if_in:NxF
          \g_@@_loaded_langfiles_seq
          { \tl_use:c { \@@_language_varname:n {#1} } }
          {
            \exp_args:Nx \file_get:nnNTF
              {
                zref-clever-
                \tl_use:c { \@@_language_varname:n {#1} }
                .lang
              }
              { \ExplSyntaxOn }
              \l_tmpa_tl
              {
                \tl_set:Nn \l_@@_setup_language_tl {#1}
                \tl_clear:N \l_@@_setup_type_tl
                \@@_opt_seq_get:cNF
                  {
                    \@@_opt_varname_language:nnn
                      {#1} { declension } { seq }
                  }
                  \l_@@_lang_declension_seq
                  { \seq_clear:N \l_@@_lang_declension_seq }
                \seq_if_empty:NTF \l_@@_lang_declension_seq
                  { \tl_clear:N \l_@@_lang_decl_case_tl }
                  {
                    \seq_get_left:NN \l_@@_lang_declension_seq
                      \l_@@_lang_decl_case_tl
                  }
                \@@_opt_seq_get:cNF
                  {
                    \@@_opt_varname_language:nnn
                      {#1} { gender } { seq }
                  }
                  \l_@@_lang_gender_seq
                  { \seq_clear:N \l_@@_lang_gender_seq }
                \keys_set:nV { zref-clever/langfile } \l_tmpa_tl
                \seq_gput_right:Nx \g_@@_loaded_langfiles_seq
                  { \tl_use:c { \@@_language_varname:n {#1} } }
                \msg_info:nnx { zref-clever } { langfile-loaded }
                  { \tl_use:c { \@@_language_varname:n {#1} } }
              }
              {
%    \end{macrocode}
% Even if we don't have the actual language file, we register it as
% ``loaded''.  At this point, it is a known language, properly declared.
% There is no point in trying to load it multiple times, if it was not found
% the first time, it won't be the next.
%    \begin{macrocode}
                \seq_gput_right:Nx \g_@@_loaded_langfiles_seq
                  { \tl_use:c { \@@_language_varname:n {#1} } }
              }
          }
      }
    \@esphack
    \group_end:
  }
\cs_generate_variant:Nn \@@_provide_langfile:n { x }
%    \end{macrocode}
% \end{macro}
%
%
%
% The set of keys for \texttt{{zref-clever/langfile}}, which is used to
% process the language files in \cs{@@_provide_langfile:n}.  The no-op cases
% for each category have their messages sent to ``info''.  These messages
% should not occur, as long as the language files are well formed, but they're
% placed there nevertheless, and can be leveraged in regression tests.
%
%    \begin{macrocode}
\keys_define:nn { zref-clever/langfile }
  {
    type .code:n =
      {
        \tl_if_empty:nTF {#1}
          { \tl_clear:N \l_@@_setup_type_tl }
          { \tl_set:Nn \l_@@_setup_type_tl {#1} }
      } ,

    case .code:n =
      {
        \seq_if_empty:NTF \l_@@_lang_declension_seq
          {
            \msg_info:nnxx { zref-clever } { language-no-decl-setup }
              { \l_@@_setup_language_tl } {#1}
          }
          {
            \seq_if_in:NnTF \l_@@_lang_declension_seq {#1}
              { \tl_set:Nn \l_@@_lang_decl_case_tl {#1} }
              {
                \msg_info:nnxx { zref-clever } { unknown-decl-case }
                  {#1} { \l_@@_setup_language_tl }
                \seq_get_left:NN \l_@@_lang_declension_seq
                  \l_@@_lang_decl_case_tl
              }
          }
      } ,
    case .value_required:n = true ,

    gender .value_required:n = true ,
    gender .code:n =
      {
        \seq_if_empty:NTF \l_@@_lang_gender_seq
          {
            \msg_info:nnxxx { zref-clever } { language-no-gender }
              { \l_@@_setup_language_tl } { gender } {#1}
          }
          {
            \tl_if_empty:NTF \l_@@_setup_type_tl
              {
                \msg_info:nnn { zref-clever }
                  { option-only-type-specific } { gender }
              }
              {
                \seq_clear:N \l_tmpa_seq
                \clist_map_inline:nn {#1}
                  {
                    \seq_if_in:NnTF \l_@@_lang_gender_seq {##1}
                      { \seq_put_right:Nn \l_tmpa_seq {##1} }
                      {
                        \msg_info:nnxx { zref-clever }
                          { gender-not-declared }
                          { \l_@@_setup_language_tl } {##1}
                      }
                  }
                \@@_opt_seq_if_set:cF
                  {
                    \@@_opt_varname_lang_type:eenn
                      { \l_@@_setup_language_tl }
                      { \l_@@_setup_type_tl }
                      { gender }
                      { seq }
                  }
                  {
                    \seq_new:c
                      {
                        \@@_opt_varname_lang_type:eenn
                          { \l_@@_setup_language_tl }
                          { \l_@@_setup_type_tl }
                          { gender }
                          { seq }
                      }
                    \seq_gset_eq:cN
                      {
                        \@@_opt_varname_lang_type:eenn
                          { \l_@@_setup_language_tl }
                          { \l_@@_setup_type_tl }
                          { gender }
                          { seq }
                      }
                      \l_tmpa_seq
                  }
              }
          }
      } ,
  }
\seq_map_inline:Nn
  \g_@@_rf_opts_tl_not_type_specific_seq
  {
    \keys_define:nn { zref-clever/langfile }
      {
        #1 .value_required:n = true ,
        #1 .code:n =
          {
            \tl_if_empty:NTF \l_@@_setup_type_tl
              {
                \@@_opt_tl_gset_if_new:cn
                  {
                    \@@_opt_varname_lang_default:enn
                      { \l_@@_setup_language_tl }
                      {#1} { tl }
                  }
                  {##1}
              }
              {
                \msg_info:nnn { zref-clever }
                  { option-not-type-specific } {#1}
              }
          } ,
      }
  }
\seq_map_inline:Nn
  \g_@@_rf_opts_tl_maybe_type_specific_seq
  {
    \keys_define:nn { zref-clever/langfile }
      {
        #1 .value_required:n = true ,
        #1 .code:n =
          {
            \tl_if_empty:NTF \l_@@_setup_type_tl
              {
                \@@_opt_tl_gset_if_new:cn
                  {
                    \@@_opt_varname_lang_default:enn
                      { \l_@@_setup_language_tl }
                      {#1} { tl }
                  }
                  {##1}
              }
              {
                \@@_opt_tl_gset_if_new:cn
                  {
                    \@@_opt_varname_lang_type:eenn
                      { \l_@@_setup_language_tl }
                      { \l_@@_setup_type_tl }
                      {#1} { tl }
                  }
                  {##1}
              }
          } ,
      }
  }
\keys_define:nn { zref-clever/langfile }
  {
    endrange .value_required:n = true ,
    endrange .code:n =
      {
        \str_case:nnF {#1}
          {
            { ref }
            {
              \tl_if_empty:NTF \l_@@_setup_type_tl
                {
                  \@@_opt_tl_gclear_if_new:c
                    {
                      \@@_opt_varname_lang_default:enn
                        { \l_@@_setup_language_tl }
                        { endrangefunc } { tl }
                    }
                  \@@_opt_tl_gclear_if_new:c
                    {
                      \@@_opt_varname_lang_default:enn
                        { \l_@@_setup_language_tl }
                        { endrangeprop } { tl }
                    }
                }
                {
                  \@@_opt_tl_gclear_if_new:c
                    {
                      \@@_opt_varname_lang_type:eenn
                        { \l_@@_setup_language_tl }
                        { \l_@@_setup_type_tl }
                        { endrangefunc } { tl }
                    }
                  \@@_opt_tl_gclear_if_new:c
                    {
                      \@@_opt_varname_lang_type:eenn
                        { \l_@@_setup_language_tl }
                        { \l_@@_setup_type_tl }
                        { endrangeprop } { tl }
                    }
                }
            }

            { stripprefix }
            {
              \tl_if_empty:NTF \l_@@_setup_type_tl
                {
                  \@@_opt_tl_gset_if_new:cn
                    {
                      \@@_opt_varname_lang_default:enn
                        { \l_@@_setup_language_tl }
                        { endrangefunc } { tl }
                    }
                    { @@_get_endrange_stripprefix }
                  \@@_opt_tl_gclear_if_new:c
                    {
                      \@@_opt_varname_lang_default:enn
                        { \l_@@_setup_language_tl }
                        { endrangeprop } { tl }
                    }
                }
                {
                  \@@_opt_tl_gset_if_new:cn
                    {
                      \@@_opt_varname_lang_type:eenn
                        { \l_@@_setup_language_tl }
                        { \l_@@_setup_type_tl }
                        { endrangefunc } { tl }
                    }
                    { @@_get_endrange_stripprefix }
                  \@@_opt_tl_gclear_if_new:c
                    {
                      \@@_opt_varname_lang_type:eenn
                        { \l_@@_setup_language_tl }
                        { \l_@@_setup_type_tl }
                        { endrangeprop } { tl }
                    }
                }
            }

            { pagecomp }
            {
              \tl_if_empty:NTF \l_@@_setup_type_tl
                {
                  \@@_opt_tl_gset_if_new:cn
                    {
                      \@@_opt_varname_lang_default:enn
                        { \l_@@_setup_language_tl }
                        { endrangefunc } { tl }
                    }
                    { @@_get_endrange_pagecomp }
                  \@@_opt_tl_gclear_if_new:c
                    {
                      \@@_opt_varname_lang_default:enn
                        { \l_@@_setup_language_tl }
                        { endrangeprop } { tl }
                    }
                }
                {
                  \@@_opt_tl_gset_if_new:cn
                    {
                      \@@_opt_varname_lang_type:eenn
                        { \l_@@_setup_language_tl }
                        { \l_@@_setup_type_tl }
                        { endrangefunc } { tl }
                    }
                    { @@_get_endrange_pagecomp }
                  \@@_opt_tl_gclear_if_new:c
                    {
                      \@@_opt_varname_lang_type:eenn
                        { \l_@@_setup_language_tl }
                        { \l_@@_setup_type_tl }
                        { endrangeprop } { tl }
                    }
                }
            }

            { pagecomp2 }
            {
              \tl_if_empty:NTF \l_@@_setup_type_tl
                {
                  \@@_opt_tl_gset_if_new:cn
                    {
                      \@@_opt_varname_lang_default:enn
                        { \l_@@_setup_language_tl }
                        { endrangefunc } { tl }
                    }
                    { @@_get_endrange_pagecomptwo }
                  \@@_opt_tl_gclear_if_new:c
                    {
                      \@@_opt_varname_lang_default:enn
                        { \l_@@_setup_language_tl }
                        { endrangeprop } { tl }
                    }
                }
                {
                  \@@_opt_tl_gset_if_new:cn
                    {
                      \@@_opt_varname_lang_type:eenn
                        { \l_@@_setup_language_tl }
                        { \l_@@_setup_type_tl }
                        { endrangefunc } { tl }
                    }
                    { @@_get_endrange_pagecomptwo }
                  \@@_opt_tl_gclear_if_new:c
                    {
                      \@@_opt_varname_lang_type:eenn
                        { \l_@@_setup_language_tl }
                        { \l_@@_setup_type_tl }
                        { endrangeprop } { tl }
                    }
                }
            }
          }
          {
            \tl_if_empty:nTF {#1}
              {
                \msg_info:nnn { zref-clever }
                  { endrange-property-undefined } {#1}
              }
              {
                \zref@ifpropundefined {#1}
                  {
                    \msg_info:nnn { zref-clever }
                      { endrange-property-undefined } {#1}
                  }
                  {
                    \tl_if_empty:NTF \l_@@_setup_type_tl
                      {
                        \@@_opt_tl_gset_if_new:cn
                          {
                            \@@_opt_varname_lang_default:enn
                              { \l_@@_setup_language_tl }
                              { endrangefunc } { tl }
                          }
                          { @@_get_endrange_property }
                        \@@_opt_tl_gset_if_new:cn
                          {
                            \@@_opt_varname_lang_default:enn
                              { \l_@@_setup_language_tl }
                              { endrangeprop } { tl }
                          }
                          {#1}
                      }
                      {
                        \@@_opt_tl_gset_if_new:cn
                          {
                            \@@_opt_varname_lang_type:eenn
                              { \l_@@_setup_language_tl }
                              { \l_@@_setup_type_tl }
                              { endrangefunc } { tl }
                          }
                          { @@_get_endrange_property }
                        \@@_opt_tl_gset_if_new:cn
                          {
                            \@@_opt_varname_lang_type:eenn
                              { \l_@@_setup_language_tl }
                              { \l_@@_setup_type_tl }
                              { endrangeprop } { tl }
                          }
                          {#1}
                      }
                  }
              }
          }
      } ,
  }
\seq_map_inline:Nn
  \g_@@_rf_opts_tl_type_names_seq
  {
    \keys_define:nn { zref-clever/langfile }
      {
        #1 .value_required:n = true ,
        #1 .code:n =
          {
            \tl_if_empty:NTF \l_@@_setup_type_tl
              {
                \msg_info:nnn { zref-clever }
                  { option-only-type-specific } {#1}
              }
              {
                \tl_if_empty:NTF \l_@@_lang_decl_case_tl
                  {
                    \@@_opt_tl_gset_if_new:cn
                      {
                        \@@_opt_varname_lang_type:eenn
                          { \l_@@_setup_language_tl }
                          { \l_@@_setup_type_tl }
                          {#1} { tl }
                      }
                      {##1}
                  }
                  {
                    \@@_opt_tl_gset_if_new:cn
                      {
                        \@@_opt_varname_lang_type:eeen
                          { \l_@@_setup_language_tl }
                          { \l_@@_setup_type_tl }
                          { \l_@@_lang_decl_case_tl - #1 } { tl }
                      }
                      {##1}
                  }
              }
          } ,
      }
  }
\seq_map_inline:Nn
  \g_@@_rf_opts_seq_refbounds_seq
  {
    \keys_define:nn { zref-clever/langfile }
      {
        #1 .value_required:n = true ,
        #1 .code:n =
          {
            \tl_if_empty:NTF \l_@@_setup_type_tl
              {
                \@@_opt_seq_if_set:cF
                  {
                    \@@_opt_varname_lang_default:enn
                      { \l_@@_setup_language_tl } {#1} { seq }
                  }
                  {
                    \seq_gclear:N \g_tmpa_seq
                    \@@_opt_seq_gset_clist_split:Nn
                      \g_tmpa_seq {##1}
                    \bool_lazy_or:nnTF
                      { \tl_if_empty_p:n {##1} }
                      {
                        \int_compare_p:nNn
                          { \seq_count:N \g_tmpa_seq } = { 4 }
                      }
                      {
                        \@@_opt_seq_gset_eq:cN
                          {
                            \@@_opt_varname_lang_default:enn
                              { \l_@@_setup_language_tl }
                              {#1} { seq }
                          }
                          \g_tmpa_seq
                      }
                      {
                        \msg_info:nnxx { zref-clever }
                          { refbounds-must-be-four }
                          {#1} { \seq_count:N \g_tmpa_seq }
                      }
                  }
              }
              {
                \@@_opt_seq_if_set:cF
                  {
                    \@@_opt_varname_lang_type:eenn
                      { \l_@@_setup_language_tl }
                      { \l_@@_setup_type_tl } {#1} { seq }
                  }
                  {
                    \seq_gclear:N \g_tmpa_seq
                    \@@_opt_seq_gset_clist_split:Nn
                      \g_tmpa_seq {##1}
                    \bool_lazy_or:nnTF
                      { \tl_if_empty_p:n {##1} }
                      {
                        \int_compare_p:nNn
                          { \seq_count:N \g_tmpa_seq } = { 4 }
                      }
                      {
                        \@@_opt_seq_gset_eq:cN
                          {
                            \@@_opt_varname_lang_type:eenn
                              { \l_@@_setup_language_tl }
                              { \l_@@_setup_type_tl }
                              {#1} { seq }
                          }
                          \g_tmpa_seq
                      }
                      {
                        \msg_info:nnxx { zref-clever }
                          { refbounds-must-be-four }
                          {#1} { \seq_count:N \g_tmpa_seq }
                      }
                  }
              }
          } ,
      }
  }
\seq_map_inline:Nn
  \g_@@_rf_opts_bool_maybe_type_specific_seq
  {
    \keys_define:nn { zref-clever/langfile }
      {
        #1 .choice: ,
        #1 / true .code:n =
          {
            \tl_if_empty:NTF \l_@@_setup_type_tl
              {
                \@@_opt_bool_if_set:cF
                  {
                    \@@_opt_varname_lang_default:enn
                      { \l_@@_setup_language_tl }
                      {#1} { bool }
                  }
                  {
                    \@@_opt_bool_gset_true:c
                      {
                        \@@_opt_varname_lang_default:enn
                          { \l_@@_setup_language_tl }
                          {#1} { bool }
                      }
                  }
              }
              {
                \@@_opt_bool_if_set:cF
                  {
                    \@@_opt_varname_lang_type:eenn
                      { \l_@@_setup_language_tl }
                      { \l_@@_setup_type_tl }
                      {#1} { bool }
                  }
                  {
                    \@@_opt_bool_gset_true:c
                      {
                        \@@_opt_varname_lang_type:eenn
                          { \l_@@_setup_language_tl }
                          { \l_@@_setup_type_tl }
                          {#1} { bool }
                      }
                  }
              }
          } ,
        #1 / false .code:n =
          {
            \tl_if_empty:NTF \l_@@_setup_type_tl
              {
                \@@_opt_bool_if_set:cF
                  {
                    \@@_opt_varname_lang_default:enn
                      { \l_@@_setup_language_tl }
                      {#1} { bool }
                  }
                  {
                    \@@_opt_bool_gset_false:c
                      {
                        \@@_opt_varname_lang_default:enn
                          { \l_@@_setup_language_tl }
                          {#1} { bool }
                      }
                  }
              }
              {
                \@@_opt_bool_if_set:cF
                  {
                    \@@_opt_varname_lang_type:eenn
                      { \l_@@_setup_language_tl }
                      { \l_@@_setup_type_tl }
                      {#1} { bool }
                  }
                  {
                    \@@_opt_bool_gset_false:c
                      {
                        \@@_opt_varname_lang_type:eenn
                          { \l_@@_setup_language_tl }
                          { \l_@@_setup_type_tl }
                          {#1} { bool }
                      }
                  }
              }
          } ,
        #1 .default:n = true ,
        no #1 .meta:n = { #1 = false } ,
        no #1 .value_forbidden:n = true ,
      }
  }
%    \end{macrocode}
%
%
%
% It is convenient for a number of language typesetting options (some basic
% separators) to have some ``fallback'' value available in case \pkg{babel} or
% \pkg{polyglossia} is loaded and sets a language which \pkg{zref-clever} does
% not know.  On the other hand, ``type names'' are not looked for in
% ``fallback'', since it is indeed impossible to provide any reasonable value
% for them for a ``specified but unknown language''.  Other typesetting
% options, for which it is not a problem being empty, need not be catered for
% with a fallback value.
%
%    \begin{macrocode}
\cs_new_protected:Npn \@@_opt_tl_cset_fallback:nn #1#2
  {
    \tl_const:cn
      { \@@_opt_varname_fallback:nn {#1} { tl } } {#2}
  }
\keyval_parse:nnn
  { }
  { \@@_opt_tl_cset_fallback:nn }
  {
    tpairsep  = {,~} ,
    tlistsep  = {,~} ,
    tlastsep  = {,~} ,
    notesep   = {~} ,
    namesep   = {\nobreakspace} ,
    pairsep   = {,~} ,
    listsep   = {,~} ,
    lastsep   = {,~} ,
    rangesep  = {\textendash} ,
  }
%    \end{macrocode}
%
%
%
% \subsection{Options}
%
%
% \subsubsection*{Auxiliary}
%
%
% \begin{macro}{\@@_prop_put_non_empty:Nnn}
%   If \meta{value} is empty, remove \meta{key} from \meta{property list}.
%   Otherwise, add \meta{key} = \meta{value} to \meta{property list}.
%   \begin{syntax}
%     \cs{@@_prop_put_non_empty:Nnn} \meta{property list} \Arg{key} \Arg{value}
%   \end{syntax}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_prop_put_non_empty:Nnn #1#2#3
  {
    \tl_if_empty:nTF {#3}
      { \prop_remove:Nn #1 {#2} }
      { \prop_put:Nnn #1 {#2} {#3} }
  }
%    \end{macrocode}
% \end{macro}
%
%
% \subsubsection*{\opt{ref} option}
%
% \cs{l_@@_ref_property_tl} stores the property to which the reference is
% being made.  Note that one thing \emph{must} be handled at this point: the
% existence of the property itself, as far as \pkg{zref} is concerned.  This
% because typesetting relies on the check \cs{zref@ifrefcontainsprop}, which
% \emph{presumes} the property is defined and silently expands the \emph{true}
% branch if it is not (insightful comments by \contributor{Ulrike Fischer} at
% \url{https://github.com/ho-tex/zref/issues/13}).  Therefore, before adding
% anything to \cs{l_@@_ref_property_tl}, check if first here with
% \cs{zref@ifpropundefined}: close it at the door.  We must also control for
% an empty value, since ``empty'' passes both \cs{zref@ifpropundefined} and
% \cs{zref@ifrefcontainsprop}.
%
%
%    \begin{macrocode}
\tl_new:N \l_@@_ref_property_tl
\keys_define:nn { zref-clever/reference }
  {
    ref .code:n =
      {
        \tl_if_empty:nTF {#1}
          {
            \msg_warning:nnn { zref-clever }
              { zref-property-undefined } {#1}
            \tl_set:Nn \l_@@_ref_property_tl { default }
          }
          {
            \zref@ifpropundefined {#1}
              {
                \msg_warning:nnn { zref-clever }
                  { zref-property-undefined } {#1}
                \tl_set:Nn \l_@@_ref_property_tl { default }
              }
              { \tl_set:Nn \l_@@_ref_property_tl {#1} }
          }
      } ,
    ref .initial:n = default ,
    ref .value_required:n = true ,
    page .meta:n = { ref = page },
    page .value_forbidden:n = true ,
  }
%    \end{macrocode}
%
%
%
% \subsubsection*{\opt{typeset} option}
%
%    \begin{macrocode}
\bool_new:N \l_@@_typeset_ref_bool
\bool_new:N \l_@@_typeset_name_bool
\keys_define:nn { zref-clever/reference }
  {
    typeset .choice: ,
    typeset / both .code:n =
      {
        \bool_set_true:N \l_@@_typeset_ref_bool
        \bool_set_true:N \l_@@_typeset_name_bool
      } ,
    typeset / ref .code:n =
      {
        \bool_set_true:N \l_@@_typeset_ref_bool
        \bool_set_false:N \l_@@_typeset_name_bool
      } ,
    typeset / name .code:n =
      {
        \bool_set_false:N \l_@@_typeset_ref_bool
        \bool_set_true:N \l_@@_typeset_name_bool
      } ,
    typeset .initial:n = both ,
    typeset .value_required:n = true ,

    noname .meta:n = { typeset = ref } ,
    noname .value_forbidden:n = true ,
    noref .meta:n = { typeset = name } ,
    noref .value_forbidden:n = true ,
  }
%    \end{macrocode}
%
%
%
% \subsubsection*{\opt{sort} option}
%
%    \begin{macrocode}
\bool_new:N \l_@@_typeset_sort_bool
\keys_define:nn { zref-clever/reference }
  {
    sort .bool_set:N = \l_@@_typeset_sort_bool ,
    sort .initial:n = true ,
    sort .default:n = true ,
    nosort .meta:n = { sort = false },
    nosort .value_forbidden:n = true ,
  }
%    \end{macrocode}
%
%
%
% \subsubsection*{\opt{typesort} option}
%
% \cs{l_@@_typesort_seq} is stored reversed, since the sort priorities are
% computed in the negative range in \cs{@@_sort_default_different_types:nn},
% so that we can implicitly rely on `0' being the ``last value'', and spare
% creating an integer variable using \cs{seq_map_indexed_inline:Nn}.
%
%    \begin{macrocode}
\seq_new:N \l_@@_typesort_seq
\keys_define:nn { zref-clever/reference }
  {
    typesort .code:n =
      {
        \seq_set_from_clist:Nn \l_@@_typesort_seq {#1}
        \seq_reverse:N \l_@@_typesort_seq
      } ,
    typesort .initial:n =
      { part , chapter , section , paragraph },
    typesort .value_required:n = true ,
    notypesort .code:n =
      { \seq_clear:N \l_@@_typesort_seq } ,
    notypesort .value_forbidden:n = true ,
  }
%    \end{macrocode}
%
%
%
% \subsubsection*{\opt{comp} option}
%
%    \begin{macrocode}
\bool_new:N \l_@@_typeset_compress_bool
\keys_define:nn { zref-clever/reference }
  {
    comp .bool_set:N = \l_@@_typeset_compress_bool ,
    comp .initial:n = true ,
    comp .default:n = true ,
    nocomp .meta:n = { comp = false },
    nocomp .value_forbidden:n = true ,
  }
%    \end{macrocode}
%
%
%
% \subsubsection*{\opt{endrange} option}
%
% The working of \opt{endrange} option depends on two underlying option values
% / variables: \texttt{endrangefunc} and \texttt{endrangeprop}.
% \texttt{endrangefunc} is the more general one, and \texttt{endrangeprop} is
% used when the first is set to \cs{@@_get_endrange_property:VVN}, which is
% the case when the user is setting \opt{endrange} to an arbitrary \pkg{zref}
% property, instead of one of the \cs{str_case:nn} matches.
%
% \texttt{endrangefunc} \emph{must} receive three arguments and, more
% specifically, its signature \emph{must} be \texttt{VVN}.  For this reason,
% \texttt{endrangefunc} should be stored without the signature, which is
% added, and hard-coded, at the calling place.  The first argument is
% \meta{beg range label}, the second \meta{end range label}, and the last
% \meta{tl var to set}.  Of course, \meta{tl var to set} must be set to a
% proper value, and that's the main task of the function.
% \texttt{endrangefunc} must also handle the case where
% \cs{zref@ifrefcontainsprop} is false, since \cs{@@_get_ref_endrange:nnN}
% cannot take care of that.  For this purpose, it may set \meta{tl var to set}
% to the special value \texttt{zc@missingproperty}, to signal a missing
% property for \cs{@@_get_ref_endrange:nnN}.
%
% An empty \texttt{endrangefunc} signals that no processing is to be made to
% the end range reference, that is, that it should be treated like any other
% one, as defined by the \opt{ref} option.  This may happen either because
% \opt{endrange} was never set for the reference type, and empty is the value
% ``returned'' by \cs{@@_get_rf_opt_tl:nnnN} for options not set, or because
% \opt{endrange} was set to \texttt{ref} at some scope which happens to get
% precedence.
%
% One thing I was divided about in this functionality was whether to
% (x-)expand the references before processing them, when such processing is
% required.  At first sight, it makes sense to do so, since we are aiming at
% ``removing common parts'' as close as possible to the printed representation
% of the references (\pkg{cleveref} does expand them in \cs{crefstripprefix}).
% On the other hand, this brings some new challenges: if a fragile command
% gets there, we are in trouble; also, if a protected one gets there, though
% things won't break as badly, we may ``strip'' the macro and stay with
% different arguments, which will then end up in the input stream.  I think
% \pkg{biblatex} is a good reference here, and it offers \cs{NumCheckSetup},
% \cs{NumsCheckSetup}, and \cs{PagesCheckSetup} aimed at locally redefining
% some commands which may interfere with the processing.  This is a good idea,
% thus we offer a similar hook for the same purpose: \texttt{endrange-setup}.
%
%    \begin{macrocode}
\NewHook { zref-clever/endrange-setup }
%    \end{macrocode}
%
%
%    \begin{macrocode}
\keys_define:nn { zref-clever/reference }
  {
    endrange .code:n =
      {
        \str_case:nnF {#1}
          {
            { ref }
            {
              \@@_opt_tl_clear:c
                {
                  \@@_opt_varname_general:nn
                    { endrangefunc } { tl }
                }
              \@@_opt_tl_clear:c
                {
                  \@@_opt_varname_general:nn
                    { endrangeprop } { tl }
                }
            }

            { stripprefix }
            {
              \@@_opt_tl_set:cn
                {
                  \@@_opt_varname_general:nn
                    { endrangefunc } { tl }
                }
                { @@_get_endrange_stripprefix }
              \@@_opt_tl_clear:c
                {
                  \@@_opt_varname_general:nn
                    { endrangeprop } { tl }
                }
            }

            { pagecomp }
            {
              \@@_opt_tl_set:cn
                {
                  \@@_opt_varname_general:nn
                    { endrangefunc } { tl }
                }
                { @@_get_endrange_pagecomp }
              \@@_opt_tl_clear:c
                {
                  \@@_opt_varname_general:nn
                    { endrangeprop } { tl }
                }
            }

            { pagecomp2 }
            {
              \@@_opt_tl_set:cn
                {
                  \@@_opt_varname_general:nn
                    { endrangefunc } { tl }
                }
                { @@_get_endrange_pagecomptwo }
              \@@_opt_tl_clear:c
                {
                  \@@_opt_varname_general:nn
                    { endrangeprop } { tl }
                }
            }

            { unset }
            {
              \@@_opt_tl_unset:c
                {
                  \@@_opt_varname_general:nn
                    { endrangefunc } { tl }
                }
              \@@_opt_tl_unset:c
                {
                  \@@_opt_varname_general:nn
                    { endrangeprop } { tl }
                }
            }
          }
          {
            \tl_if_empty:nTF {#1}
              {
                \msg_warning:nnn { zref-clever }
                  { endrange-property-undefined } {#1}
              }
              {
                \zref@ifpropundefined {#1}
                  {
                    \msg_warning:nnn { zref-clever }
                      { endrange-property-undefined } {#1}
                  }
                  {
                    \@@_opt_tl_set:cn
                      {
                        \@@_opt_varname_general:nn
                          { endrangefunc } { tl }
                      }
                      { @@_get_endrange_property }
                    \@@_opt_tl_set:cn
                      {
                        \@@_opt_varname_general:nn
                          { endrangeprop } { tl }
                      }
                      {#1}
                  }
              }
          }
      } ,
    endrange .value_required:n = true ,
  }
%    \end{macrocode}
%
%
%    \begin{macrocode}
\cs_new_protected:Npn \@@_get_endrange_property:nnN #1#2#3
  {
    \tl_if_empty:NTF \l_@@_endrangeprop_tl
      {
        \zref@ifrefcontainsprop {#2} { \l_@@_ref_property_tl }
          {
            \@@_extract_default:Nnvn #3
              {#2} { l_@@_ref_property_tl } { }
          }
          { \tl_set:Nn #3 { zc@missingproperty } }
      }
      {
        \zref@ifrefcontainsprop {#2} { \l_@@_endrangeprop_tl }
          {
%    \end{macrocode}
% If the range came about by normal compression, we already know the beginning
% and the end references share the same ``form'' and ``prefix'' (this is
% ensured at \cs{@@_labels_in_sequence:nn}), but the same is not true if the
% \opt{range} option is being used, in which case, we have to check the
% replacement \cs{l_@@_ref_property_tl} by \cs{l_@@_endrangeprop_tl} is really
% granted.
%    \begin{macrocode}
            \bool_if:NTF \l_@@_typeset_range_bool
              {
                \group_begin:
                \bool_set_false:N \l_tmpa_bool
                \exp_args:Nxx \tl_if_eq:nnT
                  {
                    \@@_extract_unexp:nnn
                      {#1} { externaldocument } { }
                  }
                  {
                    \@@_extract_unexp:nnn
                      {#2} { externaldocument } { }
                  }
                  {
                    \tl_if_eq:NnTF \l_@@_ref_property_tl { page }
                      {
                        \exp_args:Nxx \tl_if_eq:nnT
                          {
                            \@@_extract_unexp:nnn
                              {#1} { zc@pgfmt } { }
                          }
                          {
                            \@@_extract_unexp:nnn
                              {#2} { zc@pgfmt } { }
                          }
                          { \bool_set_true:N \l_tmpa_bool }
                      }
                      {
                        \exp_args:Nxx \tl_if_eq:nnT
                          {
                            \@@_extract_unexp:nnn
                              {#1} { zc@counter } { }
                          }
                          {
                            \@@_extract_unexp:nnn
                              {#2} { zc@counter } { }
                          }
                          {
                            \exp_args:Nxx \tl_if_eq:nnT
                              {
                                \@@_extract_unexp:nnn
                                  {#1} { zc@enclval } { }
                              }
                              {
                                \@@_extract_unexp:nnn
                                  {#2} { zc@enclval } { }
                              }
                              { \bool_set_true:N \l_tmpa_bool }
                          }
                      }
                  }
                \bool_if:NTF \l_tmpa_bool
                  {
                    \@@_extract_default:Nnvn \l_tmpb_tl
                      {#2} { l_@@_endrangeprop_tl } { }
                  }
                  {
                    \zref@ifrefcontainsprop
                      {#2} { \l_@@_ref_property_tl }
                      {
                        \@@_extract_default:Nnvn \l_tmpb_tl
                          {#2} { l_@@_ref_property_tl } { }
                      }
                      { \tl_set:Nn \l_tmpb_tl { zc@missingproperty } }
                  }
                \exp_args:NNNV
                  \group_end:
                  \tl_set:Nn #3 \l_tmpb_tl
              }
              {
                \@@_extract_default:Nnvn #3
                  {#2} { l_@@_endrangeprop_tl } { }
              }
          }
          {
            \zref@ifrefcontainsprop {#2} { \l_@@_ref_property_tl }
              {
                \@@_extract_default:Nnvn #3
                  {#2} { l_@@_ref_property_tl } { }
              }
              { \tl_set:Nn #3 { zc@missingproperty } }
          }
      }
  }
\cs_generate_variant:Nn \@@_get_endrange_property:nnN { VVN }
%    \end{macrocode}
%
%
%
% For the technique for smuggling the assignment out of the group, see
% \contributor{Enrico Gregorio}'s answer at
% \url{https://tex.stackexchange.com/a/56314}.
%
%    \begin{macrocode}
\cs_new_protected:Npn \@@_get_endrange_stripprefix:nnN #1#2#3
  {
    \zref@ifrefcontainsprop {#2} { \l_@@_ref_property_tl }
      {
        \group_begin:
        \UseHook { zref-clever/endrange-setup }
        \tl_set:Nx \l_tmpa_tl
          {
            \@@_extract:nnn
              {#1} { \l_@@_ref_property_tl } { }
          }
        \tl_set:Nx \l_tmpb_tl
          {
            \@@_extract:nnn
              {#2} { \l_@@_ref_property_tl } { }
          }
        \bool_set_false:N \l_tmpa_bool
        \bool_until_do:Nn \l_tmpa_bool
          {
            \exp_args:Nxx \tl_if_eq:nnTF
              { \tl_head:V \l_tmpa_tl } { \tl_head:V \l_tmpb_tl }
              {
                \tl_set:Nx \l_tmpa_tl { \tl_tail:V \l_tmpa_tl }
                \tl_set:Nx \l_tmpb_tl { \tl_tail:V \l_tmpb_tl }
                \tl_if_empty:NT \l_tmpb_tl
                  { \bool_set_true:N \l_tmpa_bool }
              }
              { \bool_set_true:N \l_tmpa_bool }
          }
        \exp_args:NNNV
          \group_end:
          \tl_set:Nn #3 \l_tmpb_tl
      }
      { \tl_set:Nn #3 { zc@missingproperty } }
  }
\cs_generate_variant:Nn \@@_get_endrange_stripprefix:nnN { VVN }
%    \end{macrocode}
%
%
% \begin{macro}{\@@_is_integer_rgx:n}
%   Test if argument is composed only of digits (adapted from
%   \url{https://tex.stackexchange.com/a/427559}).
%    \begin{macrocode}
\prg_new_protected_conditional:Npnn
  \@@_is_integer_rgx:n #1 { F , TF }
  {
    \regex_match:nnTF { \A\d+\Z } {#1}
      { \prg_return_true:  }
      { \prg_return_false: }
  }
\prg_generate_conditional_variant:Nnn
  \@@_is_integer_rgx:n { V } { F , TF }
%    \end{macrocode}
% \end{macro}
%
%
%    \begin{macrocode}
\cs_new_protected:Npn \@@_get_endrange_pagecomp:nnN #1#2#3
  {
    \zref@ifrefcontainsprop {#2} { \l_@@_ref_property_tl }
      {
        \group_begin:
        \UseHook { zref-clever/endrange-setup }
        \tl_set:Nx \l_tmpa_tl
          {
            \@@_extract:nnn
              {#1} { \l_@@_ref_property_tl } { }
          }
        \tl_set:Nx \l_tmpb_tl
          {
            \@@_extract:nnn
              {#2} { \l_@@_ref_property_tl } { }
          }
        \bool_set_false:N \l_tmpa_bool
        \@@_is_integer_rgx:VTF \l_tmpa_tl
          {
            \@@_is_integer_rgx:VF \l_tmpb_tl
              { \bool_set_true:N \l_tmpa_bool }
          }
          { \bool_set_true:N \l_tmpa_bool }
        \bool_until_do:Nn \l_tmpa_bool
          {
            \exp_args:Nxx \tl_if_eq:nnTF
              { \tl_head:V \l_tmpa_tl } { \tl_head:V \l_tmpb_tl }
              {
                \tl_set:Nx \l_tmpa_tl { \tl_tail:V \l_tmpa_tl }
                \tl_set:Nx \l_tmpb_tl { \tl_tail:V \l_tmpb_tl }
                \tl_if_empty:NT \l_tmpb_tl
                  { \bool_set_true:N \l_tmpa_bool }
              }
              { \bool_set_true:N \l_tmpa_bool }
          }
        \exp_args:NNNV
          \group_end:
          \tl_set:Nn #3 \l_tmpb_tl
      }
      { \tl_set:Nn #3 { zc@missingproperty } }
  }
\cs_generate_variant:Nn \@@_get_endrange_pagecomp:nnN { VVN }
%    \end{macrocode}
%
%
%    \begin{macrocode}
\cs_new_protected:Npn \@@_get_endrange_pagecomptwo:nnN #1#2#3
  {
    \zref@ifrefcontainsprop {#2} { \l_@@_ref_property_tl }
      {
        \group_begin:
        \UseHook { zref-clever/endrange-setup }
        \tl_set:Nx \l_tmpa_tl
          {
            \@@_extract:nnn
              {#1} { \l_@@_ref_property_tl } { }
          }
        \tl_set:Nx \l_tmpb_tl
          {
            \@@_extract:nnn
              {#2} { \l_@@_ref_property_tl } { }
          }
        \bool_set_false:N \l_tmpa_bool
        \@@_is_integer_rgx:VTF \l_tmpa_tl
          {
            \@@_is_integer_rgx:VF \l_tmpb_tl
              { \bool_set_true:N \l_tmpa_bool }
          }
          { \bool_set_true:N \l_tmpa_bool }
        \bool_until_do:Nn \l_tmpa_bool
          {
            \exp_args:Nxx \tl_if_eq:nnTF
              { \tl_head:V \l_tmpa_tl } { \tl_head:V \l_tmpb_tl }
              {
                \bool_lazy_or:nnTF
                  { \int_compare_p:nNn { \l_tmpb_tl } > { 99 } }
                  { \int_compare_p:nNn { \tl_head:V \l_tmpb_tl } = { 0 } }
                  {
                    \tl_set:Nx \l_tmpa_tl { \tl_tail:V \l_tmpa_tl }
                    \tl_set:Nx \l_tmpb_tl { \tl_tail:V \l_tmpb_tl }
                  }
                  { \bool_set_true:N \l_tmpa_bool }
              }
              { \bool_set_true:N \l_tmpa_bool }
          }
        \exp_args:NNNV
          \group_end:
          \tl_set:Nn #3 \l_tmpb_tl
      }
      { \tl_set:Nn #3 { zc@missingproperty } }
  }
\cs_generate_variant:Nn \@@_get_endrange_pagecomptwo:nnN { VVN }
%    \end{macrocode}
%
%
%
% \subsubsection*{\opt{range} and \opt{rangetopair} options}
%
% The \opt{rangetopair} option is being handled with other reference format
% option booleans at \cs{g_@@_rf_opts_bool_maybe_type_specific_seq}.
%
%    \begin{macrocode}
\bool_new:N \l_@@_typeset_range_bool
\keys_define:nn { zref-clever/reference }
  {
    range .bool_set:N = \l_@@_typeset_range_bool ,
    range .initial:n = false ,
    range .default:n = true ,
  }
%    \end{macrocode}
%
%
%
% \subsubsection*{\opt{cap} and \opt{capfirst} options}
%
% The \opt{cap} option is currently being handled with other reference format
% option booleans at \cs{g_@@_rf_opts_bool_maybe_type_specific_seq}.
%
%    \begin{macrocode}
\bool_new:N \l_@@_capfirst_bool
\keys_define:nn { zref-clever/reference }
  {
    capfirst .bool_set:N = \l_@@_capfirst_bool ,
    capfirst .initial:n = false ,
    capfirst .default:n = true ,
  }
%    \end{macrocode}
%
%
% \subsubsection*{\opt{abbrev} and \opt{noabbrevfirst} options}
%
% The \opt{abbrev} option is currently being handled with other reference
% format option booleans at \cs{g_@@_rf_opts_bool_maybe_type_specific_seq}.
%
%    \begin{macrocode}
\bool_new:N \l_@@_noabbrev_first_bool
\keys_define:nn { zref-clever/reference }
  {
    noabbrevfirst .bool_set:N = \l_@@_noabbrev_first_bool ,
    noabbrevfirst .initial:n = false ,
    noabbrevfirst .default:n = true ,
  }
%    \end{macrocode}
%
%
%
% \subsubsection*{\opt{S} option}
%
%    \begin{macrocode}
\keys_define:nn { zref-clever/reference }
  {
    S .meta:n =
      { capfirst = {#1} , noabbrevfirst = {#1} },
    S .default:n = true ,
  }
%    \end{macrocode}
%
%
% \subsubsection*{\opt{hyperref} option}
%
%    \begin{macrocode}
\bool_new:N \l_@@_hyperlink_bool
\bool_new:N \l_@@_hyperref_warn_bool
\keys_define:nn { zref-clever/reference }
  {
    hyperref .choice: ,
    hyperref / auto .code:n =
      {
        \bool_set_true:N \l_@@_hyperlink_bool
        \bool_set_false:N \l_@@_hyperref_warn_bool
      } ,
    hyperref / true .code:n =
      {
        \bool_set_true:N \l_@@_hyperlink_bool
        \bool_set_true:N \l_@@_hyperref_warn_bool
      } ,
    hyperref / false .code:n =
      {
        \bool_set_false:N \l_@@_hyperlink_bool
        \bool_set_false:N \l_@@_hyperref_warn_bool
      } ,
    hyperref .initial:n = auto ,
    hyperref .default:n = true ,
%    \end{macrocode}
% \opt{nohyperref} is provided mainly as a means to inhibit hyperlinking
% locally in \pkg{zref-vario}'s commands without the need to be setting
% \pkg{zref-clever}'s internal variables directly.  What limits setting
% \opt{hyperref} out of the preamble is that enabling hyperlinks requires
% loading packages.  But \opt{nohyperref} can only disable them, so we can use
% it in the document body too.
%    \begin{macrocode}
    nohyperref .meta:n = { hyperref = false } ,
    nohyperref .value_forbidden:n = true ,
  }
%    \end{macrocode}
%
%    \begin{macrocode}
\AddToHook { begindocument }
  {
    \@@_if_package_loaded:nTF { hyperref }
      {
        \bool_if:NT \l_@@_hyperlink_bool
          { \RequirePackage { zref-hyperref } }
      }
      {
        \bool_if:NT \l_@@_hyperref_warn_bool
          { \msg_warning:nn { zref-clever } { missing-hyperref } }
        \bool_set_false:N \l_@@_hyperlink_bool
      }
    \keys_define:nn { zref-clever/reference }
      {
        hyperref .code:n =
          { \msg_warning:nn { zref-clever } { hyperref-preamble-only } } ,
        nohyperref .code:n =
          { \bool_set_false:N \l_@@_hyperlink_bool } ,
      }
  }
%    \end{macrocode}
%
%
%
% \subsubsection*{\opt{nameinlink} option}
%
%    \begin{macrocode}
\str_new:N \l_@@_nameinlink_str
\keys_define:nn { zref-clever/reference }
  {
    nameinlink .choice: ,
    nameinlink / true .code:n =
      { \str_set:Nn \l_@@_nameinlink_str { true } } ,
    nameinlink / false .code:n =
      { \str_set:Nn \l_@@_nameinlink_str { false } } ,
    nameinlink / single .code:n =
      { \str_set:Nn \l_@@_nameinlink_str { single } } ,
    nameinlink / tsingle .code:n =
      { \str_set:Nn \l_@@_nameinlink_str { tsingle } } ,
    nameinlink .initial:n = tsingle ,
    nameinlink .default:n = true ,
  }
%    \end{macrocode}
%
%
% \subsubsection*{\opt{preposinlink} option (deprecated)}
%
%    \begin{macrocode}
\keys_define:nn { zref-clever/reference }
  {
    preposinlink .code:n =
      {
        % NOTE Option deprecated in 2022-01-12 for v0.2.0-alpha.
        \msg_warning:nnnn { zref-clever }{ option-deprecated }
          { preposinlink } { refbounds }
      } ,
  }
%    \end{macrocode}
%
%
% \subsubsection*{\opt{lang} option}
% \phantomsection{}\zlabel{sec:lang-option}
%
% The overall setup here seems a little roundabout, but this is actually
% required.  In the preamble, we (potentially) don't yet have values for the
% ``current'' and ``main'' document languages, this must be retrieved at a
% \texttt{begindocument} hook.  The \texttt{begindocument} hook is responsible
% to get values for \cs{l_@@_current_language_tl} and
% \cs{l_@@_main_language_tl}, and to set the default for
% \cs{l_@@_ref_language_tl}.  Package options, or preamble calls to
% \cs{zcsetup} are also hooked at \texttt{begindocument}, but come after the
% first hook, so that the pertinent variables have been set when they are
% executed.  Finally, we set a third \texttt{begindocument} hook, at
% \texttt{begindocument/before}, so that it runs after any options set in the
% preamble.  This hook redefines the \opt{lang} option for immediate execution
% in the document body, and ensures the \texttt{current} language's language
% file gets loaded, if it hadn't been already.
%
% For the \pkg{babel} and \pkg{polyglossia} variables which store the
% ``current'' and ``main'' languages, see
% \url{https://tex.stackexchange.com/a/233178}, including comments,
% particularly the one by Javier Bezos.  For the \pkg{babel} and
% \pkg{polyglossia} variables which store the list of loaded languages, see
% \url{https://tex.stackexchange.com/a/281220}, including comments,
% particularly PLK's.  Note, however, that languages loaded by
% \cs{babelprovide}, either directly, ``on the fly'', or with the
% \texttt{provide} option, \texttt{do not} get included in \cs{bbl@loaded}.
%
%
%    \begin{macrocode}
\AddToHook { begindocument }
  {
    \@@_if_package_loaded:nTF { babel }
      {
        \tl_set:Nn \l_@@_current_language_tl { \languagename }
        \tl_set:Nn \l_@@_main_language_tl { \bbl@main@language }
      }
      {
        \@@_if_package_loaded:nTF { polyglossia }
          {
            \tl_set:Nn \l_@@_current_language_tl { \babelname }
            \tl_set:Nn \l_@@_main_language_tl { \mainbabelname }
          }
          {
            \tl_set:Nn \l_@@_current_language_tl { english }
            \tl_set:Nn \l_@@_main_language_tl { english }
          }
      }
  }
%    \end{macrocode}
%
%
%    \begin{macrocode}
\keys_define:nn { zref-clever/reference }
  {
    lang .code:n =
      {
        \AddToHook { begindocument }
          {
            \str_case:nnF {#1}
              {
                { current }
                {
                  \tl_set:Nn \l_@@_ref_language_tl
                    { \l_@@_current_language_tl }
                }

                { main }
                {
                  \tl_set:Nn \l_@@_ref_language_tl
                    { \l_@@_main_language_tl }
                }
              }
              {
                \tl_set:Nn \l_@@_ref_language_tl {#1}
                \@@_language_if_declared:nF {#1}
                  {
                    \msg_warning:nnn { zref-clever }
                      { unknown-language-opt } {#1}
                  }
              }
            \@@_provide_langfile:x
              { \l_@@_ref_language_tl }
          }
      } ,
    lang .initial:n = current ,
    lang .value_required:n = true ,
  }
%    \end{macrocode}
%
%
%    \begin{macrocode}
\AddToHook { begindocument / before }
  {
    \AddToHook { begindocument }
      {
%    \end{macrocode}
% Redefinition of the \texttt{lang} key option for the document body.  Also,
% drop the language file loading in the document body, it is somewhat
% redundant, since \cs{@@_zcref:nnn} already ensures it.
%    \begin{macrocode}
        \keys_define:nn { zref-clever/reference }
          {
            lang .code:n =
              {
                \str_case:nnF {#1}
                  {
                    { current }
                    {
                      \tl_set:Nn \l_@@_ref_language_tl
                        { \l_@@_current_language_tl }
                    }

                    { main }
                    {
                      \tl_set:Nn \l_@@_ref_language_tl
                        { \l_@@_main_language_tl }
                    }
                  }
                  {
                    \tl_set:Nn \l_@@_ref_language_tl {#1}
                    \@@_language_if_declared:nF {#1}
                      {
                        \msg_warning:nnn { zref-clever }
                          { unknown-language-opt } {#1}
                      }
                  }
              } ,
          }
      }
  }
%    \end{macrocode}
%
%
%
% \subsubsection*{\opt{d} option}
%
% For setting the declension case.  Short for convenience and for not
% polluting the markup too much given that, for languages that need it, it may
% get to be used frequently.
%
% \contributor{\username{samcarter}} and \contributor{Alan Munn} provided
% useful comments about declension on the TeX.SX chat.  Also,
% \contributor{Florent Rougon}'s efforts in this area, with the \pkg{xcref}
% package (\url{https://github.com/frougon/xcref}), have been an insightful
% source to frame the problem in general terms.
%
%    \begin{macrocode}
\tl_new:N \l_@@_ref_decl_case_tl
\keys_define:nn { zref-clever/reference }
  {
    d .code:n =
      { \msg_warning:nnn { zref-clever } { option-document-only } { d } } ,
  }
\AddToHook { begindocument }
  {
    \keys_define:nn { zref-clever/reference }
      {
%    \end{macrocode}
% We just store the value at this point, which is validated by
% \cs{@@_process_language_settings:} after \cs{keys_set:nn}.
%    \begin{macrocode}
        d .tl_set:N = \l_@@_ref_decl_case_tl ,
        d .value_required:n = true ,
      }
  }
%    \end{macrocode}
%
%
%
% \subsubsection*{\opt{nudge} \& co.\ options}
%
%    \begin{macrocode}
\bool_new:N \l_@@_nudge_enabled_bool
\bool_new:N \l_@@_nudge_multitype_bool
\bool_new:N \l_@@_nudge_comptosing_bool
\bool_new:N \l_@@_nudge_singular_bool
\bool_new:N \l_@@_nudge_gender_bool
\tl_new:N \l_@@_ref_gender_tl
\keys_define:nn { zref-clever/reference }
  {
    nudge .choice: ,
    nudge / true .code:n =
      { \bool_set_true:N \l_@@_nudge_enabled_bool } ,
    nudge / false .code:n =
      { \bool_set_false:N \l_@@_nudge_enabled_bool } ,
    nudge / ifdraft .code:n =
      {
        \ifdraft
          { \bool_set_false:N \l_@@_nudge_enabled_bool }
          { \bool_set_true:N \l_@@_nudge_enabled_bool }
      } ,
    nudge / iffinal .code:n =
      {
        \ifoptionfinal
          { \bool_set_true:N \l_@@_nudge_enabled_bool }
          { \bool_set_false:N \l_@@_nudge_enabled_bool }
      } ,
    nudge .initial:n = false ,
    nudge .default:n = true ,
    nonudge .meta:n = { nudge = false } ,
    nonudge .value_forbidden:n = true ,
    nudgeif .code:n =
      {
        \bool_set_false:N \l_@@_nudge_multitype_bool
        \bool_set_false:N \l_@@_nudge_comptosing_bool
        \bool_set_false:N \l_@@_nudge_gender_bool
        \clist_map_inline:nn {#1}
          {
            \str_case:nnF {##1}
              {
                { multitype }
                { \bool_set_true:N \l_@@_nudge_multitype_bool }
                { comptosing }
                { \bool_set_true:N \l_@@_nudge_comptosing_bool }
                { gender }
                { \bool_set_true:N \l_@@_nudge_gender_bool }
                { all }
                {
                  \bool_set_true:N \l_@@_nudge_multitype_bool
                  \bool_set_true:N \l_@@_nudge_comptosing_bool
                  \bool_set_true:N \l_@@_nudge_gender_bool
                }
              }
              {
                \msg_warning:nnn { zref-clever }
                  { nudgeif-unknown-value } {##1}
              }
          }
      } ,
    nudgeif .value_required:n = true ,
    nudgeif .initial:n = all ,
    sg .bool_set:N = \l_@@_nudge_singular_bool ,
    sg .initial:n = false ,
    sg .default:n = true ,
    g .code:n =
      { \msg_warning:nnn { zref-clever } { option-document-only } { g } } ,
  }
\AddToHook { begindocument }
  {
    \keys_define:nn { zref-clever/reference }
      {
%    \end{macrocode}
% We just store the value at this point, which is validated by
% \cs{@@_process_language_settings:} after \cs{keys_set:nn}.
%    \begin{macrocode}
        g .tl_set:N = \l_@@_ref_gender_tl ,
        g .value_required:n = true ,
      }
  }
%    \end{macrocode}
%
%
% \subsubsection*{\opt{font} option}
%
%    \begin{macrocode}
\tl_new:N \l_@@_ref_typeset_font_tl
\keys_define:nn { zref-clever/reference }
  { font .tl_set:N = \l_@@_ref_typeset_font_tl }
%    \end{macrocode}
%
%
% \subsubsection*{\opt{titleref} option}
%
%    \begin{macrocode}
\keys_define:nn { zref-clever/reference }
  {
    titleref .code:n =
      {
        % NOTE Option deprecated in 2022-04-22 for 0.3.0.
        \msg_warning:nnxx { zref-clever }{ option-deprecated } { titleref }
          { \iow_char:N\\usepackage\iow_char:N\{zref-titleref\iow_char:N\} }
      } ,
  }
%    \end{macrocode}
%
%
% \subsubsection*{\opt{vario} option}
%
%    \begin{macrocode}
\keys_define:nn { zref-clever/reference }
  {
    vario .code:n =
      {
        % NOTE Option deprecated in 2022-04-22 for 0.3.0.
        \msg_warning:nnxx { zref-clever }{ option-deprecated } { vario }
          { \iow_char:N\\usepackage\iow_char:N\{zref-vario\iow_char:N\} }
      } ,
  }
%    \end{macrocode}
%
%
% \subsubsection*{\opt{note} option}
%
%    \begin{macrocode}
\tl_new:N \l_@@_zcref_note_tl
\keys_define:nn { zref-clever/reference }
  {
    note .tl_set:N = \l_@@_zcref_note_tl ,
    note .value_required:n = true ,
  }
%    \end{macrocode}
%
%
% \subsubsection*{\opt{check} option}
%
% Integration with \pkg{zref-check}.
%
%    \begin{macrocode}
\bool_new:N \l_@@_zrefcheck_available_bool
\bool_new:N \l_@@_zcref_with_check_bool
\keys_define:nn { zref-clever/reference }
  {
    check .code:n =
      { \msg_warning:nnn { zref-clever } { option-document-only } { check } } ,
  }
\AddToHook { begindocument }
  {
    \@@_if_package_loaded:nTF { zref-check }
      {
        \IfPackageAtLeastTF { zref-check } { 2021-09-16 }
          {
            \bool_set_true:N \l_@@_zrefcheck_available_bool
            \keys_define:nn { zref-clever/reference }
              {
                check .code:n =
                  {
                    \bool_set_true:N \l_@@_zcref_with_check_bool
                    \keys_set:nn { zref-check / zcheck } {#1}
                  } ,
                check .value_required:n = true ,
              }
          }
          {
            \bool_set_false:N \l_@@_zrefcheck_available_bool
            \keys_define:nn { zref-clever/reference }
              {
                check .code:n =
                  {
                    \msg_warning:nnn { zref-clever }
                      { zref-check-too-old } { 2021-09-16~v0.2.1 }
                  } ,
              }
          }
      }
      {
        \bool_set_false:N \l_@@_zrefcheck_available_bool
        \keys_define:nn { zref-clever/reference }
          {
            check .code:n =
              { \msg_warning:nn { zref-clever } { missing-zref-check } } ,
          }
      }
  }
%    \end{macrocode}
%
%
% \subsubsection*{\opt{reftype} option}
%
% This allows one to manually specify the reference type.  It is the
% equivalent of \pkg{cleveref}'s optional argument to \cs{label}.
%
%    \begin{macrocode}
\tl_new:N \l_@@_reftype_override_tl
\keys_define:nn { zref-clever/label }
  {
    reftype .tl_set:N = \l_@@_reftype_override_tl ,
    reftype .default:n = {} ,
    reftype .initial:n = {} ,
  }
%    \end{macrocode}
%
%
% \subsubsection*{\opt{countertype} option}
%
% \cs{l_@@_counter_type_prop} is used by \texttt{zc@type} property, and stores
% a mapping from ``counter'' to ``reference type''.  Only those counters whose
% type name is different from that of the counter need to be specified, since
% \texttt{zc@type} presumes the counter as the type if the counter is not
% found in \cs{l_@@_counter_type_prop}.
%
%    \begin{macrocode}
\prop_new:N \l_@@_counter_type_prop
\keys_define:nn { zref-clever/label }
  {
    countertype .code:n =
      {
        \keyval_parse:nnn
          {
            \msg_warning:nnnn { zref-clever }
              { key-requires-value } { countertype }
          }
          {
            \@@_prop_put_non_empty:Nnn
              \l_@@_counter_type_prop
          }
          {#1}
      } ,
    countertype .value_required:n = true ,
    countertype .initial:n =
      {
        subsection    = section ,
        subsubsection = section ,
        subparagraph  = paragraph ,
        enumi         = item ,
        enumii        = item ,
        enumiii       = item ,
        enumiv        = item ,
        mpfootnote    = footnote ,
      } ,
  }
%    \end{macrocode}
%
% One interesting comment I received (by \contributor{Denis Bitouzé}, at
% \githubissue{1}) about the most appropriate type for \texttt{paragraph} and
% \texttt{subparagraph} counters was that the reader of the document does not
% care whether that particular document structure element has been introduced
% by \cs{paragraph} or, e.g.\ by the \cs{subsubsection} command.  This is a
% difference the author knows, as they're using \LaTeX{}, but to the reader
% the difference between them is not really relevant, and it may be just
% confusing to refer to them by different names.  In this case the type for
% \texttt{paragraph} and \texttt{subparagraph} should just be
% \texttt{section}.  I don't have a strong opinion about this, and the matter
% was not pursued further.  Besides, I presume not many people would set
% \texttt{secnumdepth} so high to start with.  But, for the time being, I left
% the \texttt{paragraph} type for them, since there is actually a visual
% difference to the reader between the \cs{subsubsection} and \cs{paragraph}
% in the standard classes: up to the former, the sectioning commands break a
% line before the following text, while, from the later on, the sectioning
% commands and the following text are part of the same line.  So,
% \cs{paragraph} is actually different from ``just a shorter way to write
% \cs{subsubsubsection}''.
%
%
% \subsubsection*{\opt{counterresetters} option}
%
% \cs{l_@@_counter_resetters_seq} is used by \cs{@@_counter_reset_by:n} to
% populate the \texttt{zc@enclval} property, and stores the list of counters
% which are potential ``enclosing counters'' for other counters.  This option
% is constructed such that users can only \emph{add} items to the variable.
% There would be little gain and some risk in allowing removal, and the syntax
% of the option would become unnecessarily more complicated.  Besides, users
% can already override, for any particular counter, the search done from the
% set in \cs{l_@@_counter_resetters_seq} with the \opt{counterresetby} option.
%
%    \begin{macrocode}
\seq_new:N \l_@@_counter_resetters_seq
\keys_define:nn { zref-clever/label }
  {
    counterresetters .code:n =
      {
        \clist_map_inline:nn {#1}
          {
            \seq_if_in:NnF \l_@@_counter_resetters_seq {##1}
              {
                \seq_put_right:Nn
                  \l_@@_counter_resetters_seq {##1}
              }
          }
      } ,
    counterresetters .initial:n =
      {
        part ,
        chapter ,
        section ,
        subsection ,
        subsubsection ,
        paragraph ,
        subparagraph ,
      },
    counterresetters .value_required:n = true ,
  }
%    \end{macrocode}
%
%
%
% \subsubsection*{\opt{counterresetby} option}
%
% \cs{l_@@_counter_resetby_prop} is used by \cs{@@_counter_reset_by:n} to
% populate the \texttt{zc@enclval} property, and stores a mapping from
% counters to the counter which resets each of them.  This mapping has
% precedence in \cs{@@_counter_reset_by:n} over the search through
% \cs{l_@@_counter_resetters_seq}.
%
%    \begin{macrocode}
\prop_new:N \l_@@_counter_resetby_prop
\keys_define:nn { zref-clever/label }
  {
    counterresetby .code:n =
      {
        \keyval_parse:nnn
          {
            \msg_warning:nnn { zref-clever }
              { key-requires-value } { counterresetby }
          }
          {
            \@@_prop_put_non_empty:Nnn
              \l_@@_counter_resetby_prop
          }
          {#1}
      } ,
    counterresetby .value_required:n = true ,
    counterresetby .initial:n =
      {
%    \end{macrocode}
% The counters for the \texttt{enumerate} environment do not use the regular
% counter machinery for resetting on each level, but are nested nevertheless
% by other means, treat them as exception.
%    \begin{macrocode}
        enumii  = enumi   ,
        enumiii = enumii  ,
        enumiv  = enumiii ,
      } ,
  }
%    \end{macrocode}
%
%
% \subsubsection*{\opt{currentcounter} option}
%
% \cs{l_@@_current_counter_tl} is pretty much the starting point of all of the
% data specification for label setting done by \pkg{zref} with our setup for
% it.  It exists because we must provide some ``handle'' to specify the
% current counter for packages/features that do not set \cs{@currentcounter}
% appropriately.
%
%    \begin{macrocode}
\tl_new:N \l_@@_current_counter_tl
\keys_define:nn { zref-clever/label }
  {
    currentcounter .tl_set:N = \l_@@_current_counter_tl ,
    currentcounter .default:n = \@currentcounter ,
    currentcounter .initial:n = \@currentcounter ,
  }
%    \end{macrocode}
%
%
% \subsubsection*{\opt{nocompat} option}
%
%
%    \begin{macrocode}
\bool_new:N \g_@@_nocompat_bool
\seq_new:N \g_@@_nocompat_modules_seq
\keys_define:nn { zref-clever/reference }
  {
    nocompat .code:n =
      {
        \tl_if_empty:nTF {#1}
          { \bool_gset_true:N \g_@@_nocompat_bool }
          {
            \clist_map_inline:nn {#1}
              {
                \seq_if_in:NnF \g_@@_nocompat_modules_seq {##1}
                  {
                    \seq_gput_right:Nn
                      \g_@@_nocompat_modules_seq {##1}
                  }
              }
          }
      } ,
  }
\AddToHook { begindocument }
  {
    \keys_define:nn { zref-clever/reference }
      {
        nocompat .code:n =
          {
            \msg_warning:nnn { zref-clever }
              { option-preamble-only } { nocompat }
          }
      }
  }
\AtEndOfPackage
  {
    \AddToHook { begindocument }
      {
        \seq_map_inline:Nn \g_@@_nocompat_modules_seq
          { \msg_warning:nnn { zref-clever } { unknown-compat-module } {#1} }
      }
  }
%    \end{macrocode}
%
% \begin{macro}{\@@_compat_module:nn}
%   Function to be used for compatibility modules loading.  It should load the
%   module as long as \cs{l_@@_nocompat_bool} is false and \meta{module} is
%   not in \cs{l_@@_nocompat_modules_seq}.  The \texttt{begindocument} hook is
%   needed so that we can have the option functional along the whole preamble,
%   not just at package load time.  This requirement might be relaxed if we
%   made the option only available at load time, but this would not buy us
%   much leeway anyway, since for most compatibility modules, we must test for
%   the presence of packages at \texttt{begindocument}, only kernel features
%   and document classes could be checked reliably before that.  Besides,
%   since we are using the new hook management system, there is always its
%   functionality to deal with potential loading order issues.
%   \begin{syntax}
%     \cs{@@_compat_module:nn} \Arg{module} \Arg{code}
%   \end{syntax}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_compat_module:nn #1#2
  {
    \AddToHook { begindocument }
      {
        \bool_if:NF \g_@@_nocompat_bool
          { \seq_if_in:NnF \g_@@_nocompat_modules_seq {#1} {#2} }
        \seq_gremove_all:Nn \g_@@_nocompat_modules_seq {#1}
      }
  }
%    \end{macrocode}
% \end{macro}
%
%
%
% \subsubsection*{Reference options}
% \zlabel{sec:reference-options}
%
% This is a set of options related to reference typesetting which receive
% equal treatment and, hence, are handled in batch.  Since we are dealing with
% options to be passed to \cs{zcref} or to \cs{zcsetup} or at load time, only
% ``not necessarily type-specific'' options are pertinent here.
%
%
%    \begin{macrocode}
\seq_map_inline:Nn
  \g_@@_rf_opts_tl_reference_seq
  {
    \keys_define:nn { zref-clever/reference }
      {
        #1 .default:o = \c_novalue_tl ,
        #1 .code:n =
          {
            \tl_if_novalue:nTF {##1}
              {
                \@@_opt_tl_unset:c
                  { \@@_opt_varname_general:nn {#1} { tl } }
              }
              {
                \@@_opt_tl_set:cn
                  { \@@_opt_varname_general:nn {#1} { tl } }
                  {##1}
              }
          } ,
      }
  }
\keys_define:nn { zref-clever/reference }
  {
    refpre .code:n =
      {
        % NOTE Option deprecated in 2022-01-10 for v0.1.2-alpha.
        \msg_warning:nnnn { zref-clever }{ option-deprecated }
          { refpre } { refbounds }
      } ,
    refpos .code:n =
      {
        % NOTE Option deprecated in 2022-01-10 for v0.1.2-alpha.
        \msg_warning:nnnn { zref-clever }{ option-deprecated }
          { refpos } { refbounds }
      } ,
    preref .code:n =
      {
        % NOTE Option deprecated in 2022-01-14 for v0.2.0-alpha.
        \msg_warning:nnnn { zref-clever }{ option-deprecated }
          { preref } { refbounds }
      } ,
    postref .code:n =
      {
        % NOTE Option deprecated in 2022-01-14 for v0.2.0-alpha.
        \msg_warning:nnnn { zref-clever }{ option-deprecated }
          { postref } { refbounds }
      } ,
  }
\seq_map_inline:Nn
  \g_@@_rf_opts_seq_refbounds_seq
  {
    \keys_define:nn { zref-clever/reference }
      {
        #1 .default:o = \c_novalue_tl ,
        #1 .code:n =
          {
            \tl_if_novalue:nTF {##1}
              {
                \@@_opt_seq_unset:c
                  { \@@_opt_varname_general:nn {#1} { seq } }
              }
              {
                \seq_clear:N \l_tmpa_seq
                \@@_opt_seq_set_clist_split:Nn
                  \l_tmpa_seq {##1}
                \bool_lazy_or:nnTF
                  { \tl_if_empty_p:n {##1} }
                  { \int_compare_p:nNn { \seq_count:N \l_tmpa_seq } = { 4 } }
                  {
                    \@@_opt_seq_set_eq:cN
                      { \@@_opt_varname_general:nn {#1} { seq } }
                      \l_tmpa_seq
                  }
                  {
                    \msg_warning:nnxx { zref-clever }
                      { refbounds-must-be-four }
                      {#1} { \seq_count:N \l_tmpa_seq }
                  }
              }
          } ,
      }
  }
\seq_map_inline:Nn
  \g_@@_rf_opts_bool_maybe_type_specific_seq
  {
    \keys_define:nn { zref-clever/reference }
      {
        #1 .choice: ,
        #1 / true .code:n =
          {
            \@@_opt_bool_set_true:c
              { \@@_opt_varname_general:nn {#1} { bool } }
          } ,
        #1 / false .code:n =
          {
            \@@_opt_bool_set_false:c
              { \@@_opt_varname_general:nn {#1} { bool } }
          } ,
        #1 / unset .code:n =
          {
            \@@_opt_bool_unset:c
              { \@@_opt_varname_general:nn {#1} { bool } }
          } ,
        #1 .default:n = true ,
        no #1 .meta:n = { #1 = false } ,
        no #1 .value_forbidden:n = true ,
      }
  }
%    \end{macrocode}
%
%
% \subsubsection*{Package options}
%
% The options have been separated in two different groups, so that we can
% potentially apply them selectively to different contexts: \texttt{label} and
% \texttt{reference}.  Currently, the only use of this selection is the
% ability to exclude label related options from \cs{zcref}'s options.  Anyway,
% for package options (\cs{zcsetup}) we want the whole set, so we aggregate
% the two into \texttt{zref-clever/zcsetup}, and use that here.
%
%    \begin{macrocode}
\keys_define:nn { }
  {
    zref-clever/zcsetup .inherit:n =
      {
        zref-clever/label ,
        zref-clever/reference ,
      }
  }
%    \end{macrocode}
%
%
% \pkg{zref-clever} does not accept load-time options.  Despite the tradition
% of so doing, Joseph Wright has a point in recommending otherwise at
% \url{https://chat.stackexchange.com/transcript/message/60360822#60360822}:
% separating ``loading the package'' from ``configuring the package'' grants
% less trouble with ``option clashes'' and with expansion of options at
% load-time.
%    \begin{macrocode}
\bool_lazy_and:nnT
  { \tl_if_exist_p:c { opt@ zref-clever.sty } }
  { ! \tl_if_empty_p:c { opt@ zref-clever.sty } }
  { \msg_warning:nn { zref-clever } { load-time-options } }
%    \end{macrocode}
%
%
% \section{Configuration}
%
% \subsection{\cs{zcsetup}}
%
%
% \begin{macro}[int]{\zcsetup}
%   Provide \cs{zcsetup}.
%   \begin{syntax}
%     \cs{zcsetup}\marg{options}
%   \end{syntax}
%    \begin{macrocode}
\NewDocumentCommand \zcsetup { m }
  { \@@_zcsetup:n {#1} }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_zcsetup:n}
%   A version of \cs{zcsetup} for internal use with variant.
%   \begin{syntax}
%     \cs{@@_zcsetup:n}\marg{options}
%   \end{syntax}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_zcsetup:n #1
  { \keys_set:nn { zref-clever/zcsetup } {#1} }
\cs_generate_variant:Nn \@@_zcsetup:n { x }
%    \end{macrocode}
% \end{macro}
%
%
%
% \subsection{\cs{zcRefTypeSetup}}
% \zlabel{sec:zcreftypesetup}
%
% \cs{zcRefTypeSetup} is the main user interface for ``type-specific''
% reference formatting.  Settings done by this command have a higher
% precedence than any language-specific setting, either done at
% \cs{zcLanguageSetup} or by the package's language files.  On the other hand,
% they have a lower precedence than non type-specific general options.  The
% \meta{options} should be given in the usual \texttt{key=val} format.  The
% \meta{type} does not need to pre-exist, the property list variable to store
% the properties for the type gets created if need be.
%
% \begin{macro}[int]{\zcRefTypeSetup}
%   \begin{syntax}
%     \cs{zcRefTypeSetup} \marg{type} \marg{options}
%   \end{syntax}
%    \begin{macrocode}
\NewDocumentCommand \zcRefTypeSetup { m m }
  {
    \tl_set:Nn \l_@@_setup_type_tl {#1}
    \keys_set:nn { zref-clever/typesetup } {#2}
    \tl_clear:N \l_@@_setup_type_tl
  }
%    \end{macrocode}
% \end{macro}
%
%
%
%    \begin{macrocode}
\seq_map_inline:Nn
  \g_@@_rf_opts_tl_not_type_specific_seq
  {
    \keys_define:nn { zref-clever/typesetup }
      {
        #1 .code:n =
          {
            \msg_warning:nnn { zref-clever }
              { option-not-type-specific } {#1}
          } ,
      }
  }
\seq_map_inline:Nn
  \g_@@_rf_opts_tl_typesetup_seq
  {
    \keys_define:nn { zref-clever/typesetup }
      {
        #1 .default:o = \c_novalue_tl ,
        #1 .code:n =
          {
            \tl_if_novalue:nTF {##1}
              {
                \@@_opt_tl_unset:c
                  {
                    \@@_opt_varname_type:enn
                      { \l_@@_setup_type_tl } {#1} { tl }
                  }
              }
              {
                \@@_opt_tl_set:cn
                  {
                    \@@_opt_varname_type:enn
                      { \l_@@_setup_type_tl } {#1} { tl }
                  }
                  {##1}
              }
          } ,
      }
  }
\keys_define:nn { zref-clever/typesetup }
  {
    endrange .code:n =
      {
        \str_case:nnF {#1}
          {
            { ref }
            {
              \@@_opt_tl_clear:c
                {
                  \@@_opt_varname_type:enn
                    { \l_@@_setup_type_tl } { endrangefunc } { tl }
                }
              \@@_opt_tl_clear:c
                {
                  \@@_opt_varname_type:enn
                    { \l_@@_setup_type_tl } { endrangeprop } { tl }
                }
            }

            { stripprefix }
            {
              \@@_opt_tl_set:cn
                {
                  \@@_opt_varname_type:enn
                    { \l_@@_setup_type_tl } { endrangefunc } { tl }
                }
                { @@_get_endrange_stripprefix }
              \@@_opt_tl_clear:c
                {
                  \@@_opt_varname_type:enn
                    { \l_@@_setup_type_tl } { endrangeprop } { tl }
                }
            }

            { pagecomp }
            {
              \@@_opt_tl_set:cn
                {
                  \@@_opt_varname_type:enn
                    { \l_@@_setup_type_tl } { endrangefunc } { tl }
                }
                { @@_get_endrange_pagecomp }
              \@@_opt_tl_clear:c
                {
                  \@@_opt_varname_type:enn
                    { \l_@@_setup_type_tl } { endrangeprop } { tl }
                }
            }

            { pagecomp2 }
            {
              \@@_opt_tl_set:cn
                {
                  \@@_opt_varname_type:enn
                    { \l_@@_setup_type_tl } { endrangefunc } { tl }
                }
                { @@_get_endrange_pagecomptwo }
              \@@_opt_tl_clear:c
                {
                  \@@_opt_varname_type:enn
                    { \l_@@_setup_type_tl } { endrangeprop } { tl }
                }
            }

            { unset }
            {
              \@@_opt_tl_unset:c
                {
                  \@@_opt_varname_type:enn
                    { \l_@@_setup_type_tl } { endrangefunc } { tl }
                }
              \@@_opt_tl_unset:c
                {
                  \@@_opt_varname_type:enn
                    { \l_@@_setup_type_tl } { endrangeprop } { tl }
                }
            }
          }
          {
            \tl_if_empty:nTF {#1}
              {
                \msg_warning:nnn { zref-clever }
                  { endrange-property-undefined } {#1}
              }
              {
                \zref@ifpropundefined {#1}
                  {
                    \msg_warning:nnn { zref-clever }
                      { endrange-property-undefined } {#1}
                  }
                  {
                    \@@_opt_tl_set:cn
                      {
                        \@@_opt_varname_type:enn
                          { \l_@@_setup_type_tl }
                          { endrangefunc } { tl }
                      }
                      { @@_get_endrange_property }
                    \@@_opt_tl_set:cn
                      {
                        \@@_opt_varname_type:enn
                          { \l_@@_setup_type_tl }
                          { endrangeprop } { tl }
                      }
                      {#1}
                  }
              }
          }
      } ,
    endrange .value_required:n = true ,
  }
\keys_define:nn { zref-clever/typesetup }
  {
    refpre .code:n =
      {
        % NOTE Option deprecated in 2022-01-10 for v0.1.2-alpha.
        \msg_warning:nnnn { zref-clever }{ option-deprecated }
          { refpre } { refbounds }
      } ,
    refpos .code:n =
      {
        % NOTE Option deprecated in 2022-01-10 for v0.1.2-alpha.
        \msg_warning:nnnn { zref-clever }{ option-deprecated }
          { refpos } { refbounds }
      } ,
    preref .code:n =
      {
        % NOTE Option deprecated in 2022-01-14 for v0.2.0-alpha.
        \msg_warning:nnnn { zref-clever }{ option-deprecated }
          { preref } { refbounds }
      } ,
    postref .code:n =
      {
        % NOTE Option deprecated in 2022-01-14 for v0.2.0-alpha.
        \msg_warning:nnnn { zref-clever }{ option-deprecated }
          { postref } { refbounds }
      } ,
  }
\seq_map_inline:Nn
  \g_@@_rf_opts_seq_refbounds_seq
  {
    \keys_define:nn { zref-clever/typesetup }
      {
        #1 .default:o = \c_novalue_tl ,
        #1 .code:n =
          {
            \tl_if_novalue:nTF {##1}
              {
                \@@_opt_seq_unset:c
                  {
                    \@@_opt_varname_type:enn
                      { \l_@@_setup_type_tl } {#1} { seq }
                  }
              }
              {
                \seq_clear:N \l_tmpa_seq
                \@@_opt_seq_set_clist_split:Nn
                  \l_tmpa_seq {##1}
                \bool_lazy_or:nnTF
                  { \tl_if_empty_p:n {##1} }
                  { \int_compare_p:nNn { \seq_count:N \l_tmpa_seq } = { 4 } }
                  {
                    \@@_opt_seq_set_eq:cN
                      {
                        \@@_opt_varname_type:enn
                          { \l_@@_setup_type_tl } {#1} { seq }
                      }
                      \l_tmpa_seq
                  }
                  {
                    \msg_warning:nnxx { zref-clever }
                      { refbounds-must-be-four }
                      {#1} { \seq_count:N \l_tmpa_seq }
                  }
              }
          } ,
      }
  }
\seq_map_inline:Nn
  \g_@@_rf_opts_bool_maybe_type_specific_seq
  {
    \keys_define:nn { zref-clever/typesetup }
      {
        #1 .choice: ,
        #1 / true .code:n =
          {
            \@@_opt_bool_set_true:c
              {
                \@@_opt_varname_type:enn
                  { \l_@@_setup_type_tl }
                  {#1} { bool }
              }
          } ,
        #1 / false .code:n =
          {
            \@@_opt_bool_set_false:c
              {
                \@@_opt_varname_type:enn
                  { \l_@@_setup_type_tl }
                  {#1} { bool }
              }
          } ,
        #1 / unset .code:n =
          {
            \@@_opt_bool_unset:c
              {
                \@@_opt_varname_type:enn
                  { \l_@@_setup_type_tl }
                  {#1} { bool }
              }
          } ,
        #1 .default:n = true ,
        no #1 .meta:n = { #1 = false } ,
        no #1 .value_forbidden:n = true ,
      }
  }
%    \end{macrocode}
%
%
% \subsection{\cs{zcLanguageSetup}}
%
% \cs{zcLanguageSetup} is the main user interface for ``language-specific''
% reference formatting, be it ``type-specific'' or not.  The difference
% between the two cases is captured by the \texttt{type} key, which works as a
% sort of a ``switch''.  Inside the \meta{options} argument of
% \cs{zcLanguageSetup}, any options made before the first \texttt{type} key
% declare ``default'' (non type-specific) language options.  When the
% \texttt{type} key is given with a value, the options following it will set
% ``type-specific'' language options for that type.  The current type can be
% switched off by an empty \texttt{type} key.  \cs{zcLanguageSetup} is
% preamble only.
%
% \begin{macro}[int]{\zcLanguageSetup}
%   \begin{syntax}
%     \cs{zcLanguageSetup}\marg{language}\marg{options}
%   \end{syntax}
%    \begin{macrocode}
\NewDocumentCommand \zcLanguageSetup { m m }
  {
    \group_begin:
    \@@_language_if_declared:nTF {#1}
      {
        \tl_clear:N \l_@@_setup_type_tl
        \tl_set:Nn \l_@@_setup_language_tl {#1}
        \@@_opt_seq_get:cNF
          {
            \@@_opt_varname_language:nnn
              {#1} { declension } { seq }
          }
          \l_@@_lang_declension_seq
          { \seq_clear:N \l_@@_lang_declension_seq }
        \seq_if_empty:NTF \l_@@_lang_declension_seq
          { \tl_clear:N \l_@@_lang_decl_case_tl }
          {
            \seq_get_left:NN \l_@@_lang_declension_seq
              \l_@@_lang_decl_case_tl
          }
        \@@_opt_seq_get:cNF
          {
            \@@_opt_varname_language:nnn
              {#1} { gender } { seq }
          }
          \l_@@_lang_gender_seq
          { \seq_clear:N \l_@@_lang_gender_seq }
        \keys_set:nn { zref-clever/langsetup } {#2}
      }
      { \msg_warning:nnn { zref-clever } { unknown-language-setup } {#1} }
    \group_end:
  }
\@onlypreamble \zcLanguageSetup
%    \end{macrocode}
% \end{macro}
%
%
%
% The set of keys for \texttt{{zref-clever/langsetup}}, which is used to set
% language-specific options in \cs{zcLanguageSetup}.
%
%    \begin{macrocode}
\keys_define:nn { zref-clever/langsetup }
  {
    type .code:n =
      {
        \tl_if_empty:nTF {#1}
          { \tl_clear:N \l_@@_setup_type_tl }
          { \tl_set:Nn \l_@@_setup_type_tl {#1} }
      } ,

    case .code:n =
      {
        \seq_if_empty:NTF \l_@@_lang_declension_seq
          {
            \msg_warning:nnxx { zref-clever } { language-no-decl-setup }
              { \l_@@_setup_language_tl } {#1}
          }
          {
            \seq_if_in:NnTF \l_@@_lang_declension_seq {#1}
              { \tl_set:Nn \l_@@_lang_decl_case_tl {#1} }
              {
                \msg_warning:nnxx { zref-clever } { unknown-decl-case }
                  {#1} { \l_@@_setup_language_tl }
                \seq_get_left:NN \l_@@_lang_declension_seq
                  \l_@@_lang_decl_case_tl
              }
          }
      } ,
    case .value_required:n = true ,

    gender .value_required:n = true ,
    gender .code:n =
      {
        \seq_if_empty:NTF \l_@@_lang_gender_seq
          {
            \msg_warning:nnxxx { zref-clever } { language-no-gender }
              { \l_@@_setup_language_tl } { gender } {#1}
          }
          {
            \tl_if_empty:NTF \l_@@_setup_type_tl
              {
                \msg_warning:nnn { zref-clever }
                  { option-only-type-specific } { gender }
              }
              {
                \seq_clear:N \l_tmpa_seq
                \clist_map_inline:nn {#1}
                  {
                    \seq_if_in:NnTF \l_@@_lang_gender_seq {##1}
                      { \seq_put_right:Nn \l_tmpa_seq {##1} }
                      {
                        \msg_warning:nnxx { zref-clever }
                          { gender-not-declared }
                          { \l_@@_setup_language_tl } {##1}
                      }
                  }
                \@@_opt_seq_gset_eq:cN
                  {
                    \@@_opt_varname_lang_type:eenn
                      { \l_@@_setup_language_tl }
                      { \l_@@_setup_type_tl }
                      { gender }
                      { seq }
                  }
                  \l_tmpa_seq
              }
          }
      } ,
  }
\seq_map_inline:Nn
  \g_@@_rf_opts_tl_not_type_specific_seq
  {
    \keys_define:nn { zref-clever/langsetup }
      {
        #1 .value_required:n = true ,
        #1 .code:n =
          {
            \tl_if_empty:NTF \l_@@_setup_type_tl
              {
                \@@_opt_tl_gset:cn
                  {
                    \@@_opt_varname_lang_default:enn
                      { \l_@@_setup_language_tl } {#1} { tl }
                  }
                  {##1}
              }
              {
                \msg_warning:nnn { zref-clever }
                  { option-not-type-specific } {#1}
              }
          } ,
      }
  }
\seq_map_inline:Nn
  \g_@@_rf_opts_tl_maybe_type_specific_seq
  {
    \keys_define:nn { zref-clever/langsetup }
      {
        #1 .value_required:n = true ,
        #1 .code:n =
          {
            \tl_if_empty:NTF \l_@@_setup_type_tl
              {
                \@@_opt_tl_gset:cn
                  {
                    \@@_opt_varname_lang_default:enn
                      { \l_@@_setup_language_tl } {#1} { tl }
                  }
                  {##1}
              }
              {
                \@@_opt_tl_gset:cn
                  {
                    \@@_opt_varname_lang_type:eenn
                      { \l_@@_setup_language_tl }
                      { \l_@@_setup_type_tl }
                      {#1} { tl }
                  }
                  {##1}
              }
          } ,
      }
  }
\keys_define:nn { zref-clever/langsetup }
  {
    endrange .value_required:n = true ,
    endrange .code:n =
      {
        \str_case:nnF {#1}
          {
            { ref }
            {
              \tl_if_empty:NTF \l_@@_setup_type_tl
                {
                  \@@_opt_tl_gclear:c
                    {
                      \@@_opt_varname_lang_default:enn
                        { \l_@@_setup_language_tl }
                        { endrangefunc } { tl }
                    }
                  \@@_opt_tl_gclear:c
                    {
                      \@@_opt_varname_lang_default:enn
                        { \l_@@_setup_language_tl }
                        { endrangeprop } { tl }
                    }
                }
                {
                  \@@_opt_tl_gclear:c
                    {
                      \@@_opt_varname_lang_type:eenn
                        { \l_@@_setup_language_tl }
                        { \l_@@_setup_type_tl }
                        { endrangefunc } { tl }
                    }
                  \@@_opt_tl_gclear:c
                    {
                      \@@_opt_varname_lang_type:eenn
                        { \l_@@_setup_language_tl }
                        { \l_@@_setup_type_tl }
                        { endrangeprop } { tl }
                    }
                }
            }

            { stripprefix }
            {
              \tl_if_empty:NTF \l_@@_setup_type_tl
                {
                  \@@_opt_tl_gset:cn
                    {
                      \@@_opt_varname_lang_default:enn
                        { \l_@@_setup_language_tl }
                        { endrangefunc } { tl }
                    }
                    { @@_get_endrange_stripprefix }
                  \@@_opt_tl_gclear:c
                    {
                      \@@_opt_varname_lang_default:enn
                        { \l_@@_setup_language_tl }
                        { endrangeprop } { tl }
                    }
                }
                {
                  \@@_opt_tl_gset:cn
                    {
                      \@@_opt_varname_lang_type:eenn
                        { \l_@@_setup_language_tl }
                        { \l_@@_setup_type_tl }
                        { endrangefunc } { tl }
                    }
                    { @@_get_endrange_stripprefix }
                  \@@_opt_tl_gclear:c
                    {
                      \@@_opt_varname_lang_type:eenn
                        { \l_@@_setup_language_tl }
                        { \l_@@_setup_type_tl }
                        { endrangeprop } { tl }
                    }
                }
            }

            { pagecomp }
            {
              \tl_if_empty:NTF \l_@@_setup_type_tl
                {
                  \@@_opt_tl_gset:cn
                    {
                      \@@_opt_varname_lang_default:enn
                        { \l_@@_setup_language_tl }
                        { endrangefunc } { tl }
                    }
                    { @@_get_endrange_pagecomp }
                  \@@_opt_tl_gclear:c
                    {
                      \@@_opt_varname_lang_default:enn
                        { \l_@@_setup_language_tl }
                        { endrangeprop } { tl }
                    }
                }
                {
                  \@@_opt_tl_gset:cn
                    {
                      \@@_opt_varname_lang_type:eenn
                        { \l_@@_setup_language_tl }
                        { \l_@@_setup_type_tl }
                        { endrangefunc } { tl }
                    }
                    { @@_get_endrange_pagecomp }
                  \@@_opt_tl_gclear:c
                    {
                      \@@_opt_varname_lang_type:eenn
                        { \l_@@_setup_language_tl }
                        { \l_@@_setup_type_tl }
                        { endrangeprop } { tl }
                    }
                }
            }

            { pagecomp2 }
            {
              \tl_if_empty:NTF \l_@@_setup_type_tl
                {
                  \@@_opt_tl_gset:cn
                    {
                      \@@_opt_varname_lang_default:enn
                        { \l_@@_setup_language_tl }
                        { endrangefunc } { tl }
                    }
                    { @@_get_endrange_pagecomptwo }
                  \@@_opt_tl_gclear:c
                    {
                      \@@_opt_varname_lang_default:enn
                        { \l_@@_setup_language_tl }
                        { endrangeprop } { tl }
                    }
                }
                {
                  \@@_opt_tl_gset:cn
                    {
                      \@@_opt_varname_lang_type:eenn
                        { \l_@@_setup_language_tl }
                        { \l_@@_setup_type_tl }
                        { endrangefunc } { tl }
                    }
                    { @@_get_endrange_pagecomptwo }
                  \@@_opt_tl_gclear:c
                    {
                      \@@_opt_varname_lang_type:eenn
                        { \l_@@_setup_language_tl }
                        { \l_@@_setup_type_tl }
                        { endrangeprop } { tl }
                    }
                }
            }
          }
          {
            \tl_if_empty:nTF {#1}
              {
                \msg_warning:nnn { zref-clever }
                  { endrange-property-undefined } {#1}
              }
              {
                \zref@ifpropundefined {#1}
                  {
                    \msg_warning:nnn { zref-clever }
                      { endrange-property-undefined } {#1}
                  }
                  {
                    \tl_if_empty:NTF \l_@@_setup_type_tl
                      {
                        \@@_opt_tl_gset:cn
                          {
                            \@@_opt_varname_lang_default:enn
                              { \l_@@_setup_language_tl }
                              { endrangefunc } { tl }
                          }
                          { @@_get_endrange_property }
                        \@@_opt_tl_gset:cn
                          {
                            \@@_opt_varname_lang_default:enn
                              { \l_@@_setup_language_tl }
                              { endrangeprop } { tl }
                          }
                          {#1}
                      }
                      {
                        \@@_opt_tl_gset:cn
                          {
                            \@@_opt_varname_lang_type:eenn
                              { \l_@@_setup_language_tl }
                              { \l_@@_setup_type_tl }
                              { endrangefunc } { tl }
                          }
                          { @@_get_endrange_property }
                        \@@_opt_tl_gset:cn
                          {
                            \@@_opt_varname_lang_type:eenn
                              { \l_@@_setup_language_tl }
                              { \l_@@_setup_type_tl }
                              { endrangeprop } { tl }
                          }
                          {#1}
                      }
                  }
              }
          }
      } ,
  }
\keys_define:nn { zref-clever/langsetup }
  {
    refpre .code:n =
      {
        % NOTE Option deprecated in 2022-01-10 for v0.1.2-alpha.
        \msg_warning:nnnn { zref-clever }{ option-deprecated }
          { refpre } { refbounds }
      } ,
    refpos .code:n =
      {
        % NOTE Option deprecated in 2022-01-10 for v0.1.2-alpha.
        \msg_warning:nnnn { zref-clever }{ option-deprecated }
          { refpos } { refbounds }
      } ,
    preref .code:n =
      {
        % NOTE Option deprecated in 2022-01-14 for v0.2.0-alpha.
        \msg_warning:nnnn { zref-clever }{ option-deprecated }
          { preref } { refbounds }
      } ,
    postref .code:n =
      {
        % NOTE Option deprecated in 2022-01-14 for v0.2.0-alpha.
        \msg_warning:nnnn { zref-clever }{ option-deprecated }
          { postref } { refbounds }
      } ,
  }
\seq_map_inline:Nn
  \g_@@_rf_opts_tl_type_names_seq
  {
    \keys_define:nn { zref-clever/langsetup }
      {
        #1 .value_required:n = true ,
        #1 .code:n =
          {
            \tl_if_empty:NTF \l_@@_setup_type_tl
              {
                \msg_warning:nnn { zref-clever }
                  { option-only-type-specific } {#1}
              }
              {
                \tl_if_empty:NTF \l_@@_lang_decl_case_tl
                  {
                    \@@_opt_tl_gset:cn
                      {
                        \@@_opt_varname_lang_type:eenn
                          { \l_@@_setup_language_tl }
                          { \l_@@_setup_type_tl }
                          {#1} { tl }
                      }
                      {##1}
                  }
                  {
                    \@@_opt_tl_gset:cn
                      {
                        \@@_opt_varname_lang_type:eeen
                          { \l_@@_setup_language_tl }
                          { \l_@@_setup_type_tl }
                          { \l_@@_lang_decl_case_tl - #1 }
                          { tl }
                      }
                      {##1}
                  }
              }
          } ,
      }
  }
\seq_map_inline:Nn
  \g_@@_rf_opts_seq_refbounds_seq
  {
    \keys_define:nn { zref-clever/langsetup }
      {
        #1 .value_required:n = true ,
        #1 .code:n =
          {
            \tl_if_empty:NTF \l_@@_setup_type_tl
              {
                \seq_gclear:N \g_tmpa_seq
                \@@_opt_seq_gset_clist_split:Nn
                  \g_tmpa_seq {##1}
                \bool_lazy_or:nnTF
                  { \tl_if_empty_p:n {##1} }
                  {
                    \int_compare_p:nNn
                      { \seq_count:N \g_tmpa_seq } = { 4 }
                  }
                  {
                    \@@_opt_seq_gset_eq:cN
                      {
                        \@@_opt_varname_lang_default:enn
                          { \l_@@_setup_language_tl }
                          {#1} { seq }
                      }
                      \g_tmpa_seq
                  }
                  {
                    \msg_warning:nnxx { zref-clever }
                      { refbounds-must-be-four }
                      {#1} { \seq_count:N \g_tmpa_seq }
                  }
              }
              {
                \seq_gclear:N \g_tmpa_seq
                \@@_opt_seq_gset_clist_split:Nn
                  \g_tmpa_seq {##1}
                \bool_lazy_or:nnTF
                  { \tl_if_empty_p:n {##1} }
                  {
                    \int_compare_p:nNn
                      { \seq_count:N \g_tmpa_seq } = { 4 }
                  }
                  {
                    \@@_opt_seq_gset_eq:cN
                      {
                        \@@_opt_varname_lang_type:eenn
                          { \l_@@_setup_language_tl }
                          { \l_@@_setup_type_tl } {#1} { seq }
                      }
                      \g_tmpa_seq
                  }
                  {
                    \msg_warning:nnxx { zref-clever }
                      { refbounds-must-be-four }
                      {#1} { \seq_count:N \g_tmpa_seq }
                  }
              }
          } ,
      }
  }
\seq_map_inline:Nn
  \g_@@_rf_opts_bool_maybe_type_specific_seq
  {
    \keys_define:nn { zref-clever/langsetup }
      {
        #1 .choice: ,
        #1 / true .code:n =
          {
            \tl_if_empty:NTF \l_@@_setup_type_tl
              {
                \@@_opt_bool_gset_true:c
                  {
                    \@@_opt_varname_lang_default:enn
                      { \l_@@_setup_language_tl }
                      {#1} { bool }
                  }
              }
              {
                \@@_opt_bool_gset_true:c
                  {
                    \@@_opt_varname_lang_type:eenn
                      { \l_@@_setup_language_tl }
                      { \l_@@_setup_type_tl }
                      {#1} { bool }
                  }
              }
          } ,
        #1 / false .code:n =
          {
            \tl_if_empty:NTF \l_@@_setup_type_tl
              {
                \@@_opt_bool_gset_false:c
                  {
                    \@@_opt_varname_lang_default:enn
                      { \l_@@_setup_language_tl }
                      {#1} { bool }
                  }
              }
              {
                \@@_opt_bool_gset_false:c
                  {
                    \@@_opt_varname_lang_type:eenn
                      { \l_@@_setup_language_tl }
                      { \l_@@_setup_type_tl }
                      {#1} { bool }
                  }
              }
          } ,
        #1 .default:n = true ,
        no #1 .meta:n = { #1 = false } ,
        no #1 .value_forbidden:n = true ,
      }
  }
%    \end{macrocode}
%
%
% \section{User interface}
%
% \subsection{\cs{zcref}}
%
%
% \begin{macro}[int]{\zcref}
%   The main user command of the package.
%   \begin{syntax}
%     \cs{zcref}\meta{*}\oarg{options}\marg{labels}
%   \end{syntax}
%    \begin{macrocode}
\NewDocumentCommand \zcref { s O { } m }
  { \zref@wrapper@babel \@@_zcref:nnn {#3} {#1} {#2} }
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}{\@@_zcref:nnnn}
%   An intermediate internal function, which does the actual heavy lifting,
%   and places \Arg{labels} as first argument, so that it can be protected by
%   \cs{zref@wrapper@babel} in \cs{zcref}.
%   \begin{syntax}
%     \cs{@@_zcref:nnnn} \Arg{labels} \Arg{*} \Arg{options}
%   \end{syntax}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_zcref:nnn #1#2#3
  {
    \group_begin:
%    \end{macrocode}
% Set options.
%    \begin{macrocode}
      \keys_set:nn { zref-clever/reference } {#3}
%    \end{macrocode}
% Store arguments values.
%    \begin{macrocode}
      \seq_set_from_clist:Nn \l_@@_zcref_labels_seq {#1}
      \bool_set:Nn \l_@@_link_star_bool {#2}
%    \end{macrocode}
% Ensure language file for reference language is loaded, if available.  We
% cannot rely on \cs{keys_set:nn} for the task, since if the \opt{lang} option
% is set for \texttt{current}, the actual language may have changed outside
% our control.  \cs{@@_provide_langfile:x} does nothing if the language file
% is already loaded.
%    \begin{macrocode}
      \@@_provide_langfile:x { \l_@@_ref_language_tl }
%    \end{macrocode}
% Process language settings.
%    \begin{macrocode}
      \@@_process_language_settings:
%    \end{macrocode}
% Integration with \pkg{zref-check}.
%    \begin{macrocode}
      \bool_lazy_and:nnT
        { \l_@@_zrefcheck_available_bool }
        { \l_@@_zcref_with_check_bool }
        { \zrefcheck_zcref_beg_label: }
%    \end{macrocode}
% Sort the labels.
%    \begin{macrocode}
      \bool_lazy_or:nnT
        { \l_@@_typeset_sort_bool }
        { \l_@@_typeset_range_bool }
        { \@@_sort_labels: }
%    \end{macrocode}
% Typeset the references.  Also, set the reference font, and group it, so that
% it does not leak to the note.
%    \begin{macrocode}
      \group_begin:
      \l_@@_ref_typeset_font_tl
      \@@_typeset_refs:
      \group_end:
%    \end{macrocode}
% Typeset \texttt{note}.
%    \begin{macrocode}
      \tl_if_empty:NF \l_@@_zcref_note_tl
        {
          \@@_get_rf_opt_tl:nxxN { notesep }
            { \l_@@_label_type_a_tl }
            { \l_@@_ref_language_tl }
            \l_tmpa_tl
          \l_tmpa_tl
          \l_@@_zcref_note_tl
        }
%    \end{macrocode}
% Integration with \pkg{zref-check}.
%    \begin{macrocode}
      \bool_lazy_and:nnT
        { \l_@@_zrefcheck_available_bool }
        { \l_@@_zcref_with_check_bool }
        {
          \zrefcheck_zcref_end_label_maybe:
          \zrefcheck_zcref_run_checks_on_labels:n
            { \l_@@_zcref_labels_seq }
        }
%    \end{macrocode}
% Integration with \pkg{mathtools}.
%    \begin{macrocode}
    \bool_if:NT \l_@@_mathtools_showonlyrefs_bool
      {
        \@@_mathtools_showonlyrefs:n
          { \l_@@_zcref_labels_seq }
      }
    \group_end:
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\l_@@_zcref_labels_seq, \l_@@_link_star_bool}
%    \begin{macrocode}
\seq_new:N \l_@@_zcref_labels_seq
\bool_new:N \l_@@_link_star_bool
%    \end{macrocode}
% \end{macro}
%
%
%
% \subsection{\cs{zcpageref}}
%
%
% \begin{macro}[int]{\zcpageref}
%   A \cs{pageref} equivalent of \cs{zcref}.
%   \begin{syntax}
%     \cs{zcpageref}\meta{*}\oarg{options}\marg{labels}
%   \end{syntax}
%    \begin{macrocode}
\NewDocumentCommand \zcpageref { s O { } m }
  {
    \group_begin:
    \IfBooleanT {#1}
      { \bool_set_false:N \l_@@_hyperlink_bool }
    \zcref [#2, ref = page] {#3}
    \group_end:
  }
%    \end{macrocode}
% \end{macro}
%
%
%
% \section{Sorting}
%
% Sorting is certainly a ``big task'' for \pkg{zref-clever} but, in the end,
% it boils down to ``carefully done branching'', and quite some of it.  The
% sorting of ``page'' references is very much lightened by the availability of
% \texttt{abspage}, from the \pkg{zref-abspage} module, which offers ``just
% what we need'' for our purposes.  The sorting of ``default'' references
% falls on two main cases: i) labels of the same type; ii) labels of different
% types.  The first case is sorted according to the priorities set by the
% \opt{typesort} option or, if that is silent for the case, by the order in
% which labels were given by the user in \cs{zcref}.  The second case is the
% most involved one, since it is possible for multiple counters to be bundled
% together in a single reference type.  Because of this, sorting must take
% into account the whole chain of ``enclosing counters'' for the counters of
% the labels at hand.
%
% \begin{macro}
%   {
%     \l_@@_label_type_a_tl ,
%     \l_@@_label_type_b_tl ,
%     \l_@@_label_enclval_a_tl ,
%     \l_@@_label_enclval_b_tl ,
%     \l_@@_label_extdoc_a_tl ,
%     \l_@@_label_extdoc_b_tl ,
%   }
%   Auxiliary variables, for use in sorting, and some also in typesetting.
%   Used to store reference information -- label properties -- of the
%   ``current'' (\texttt{a}) and ``next'' (\texttt{b}) labels.
%    \begin{macrocode}
\tl_new:N \l_@@_label_type_a_tl
\tl_new:N \l_@@_label_type_b_tl
\tl_new:N \l_@@_label_enclval_a_tl
\tl_new:N \l_@@_label_enclval_b_tl
\tl_new:N \l_@@_label_extdoc_a_tl
\tl_new:N \l_@@_label_extdoc_b_tl
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\l_@@_sort_decided_bool}
%   Auxiliary variable for \cs{@@_sort_default_same_type:nn}, signals if the
%   sorting between two labels has been decided or not.
%    \begin{macrocode}
\bool_new:N \l_@@_sort_decided_bool
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\l_@@_sort_prior_a_int,\l_@@_sort_prior_b_int}
%   Auxiliary variables for \cs{@@_sort_default_different_types:nn}.  Store
%   the sort priority of the ``current'' and ``next'' labels.
%    \begin{macrocode}
\int_new:N \l_@@_sort_prior_a_int
\int_new:N \l_@@_sort_prior_b_int
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\l_@@_label_types_seq}
%   Stores the order in which reference types appear in the label list
%   supplied by the user in \cs{zcref}.  This variable is populated by
%   \cs{@@_label_type_put_new_right:n} at the start of \cs{@@_sort_labels:}.
%   This order is required as a ``last resort'' sort criterion between the
%   reference types, for use in \cs{@@_sort_default_different_types:nn}.
%    \begin{macrocode}
\seq_new:N \l_@@_label_types_seq
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}{\@@_sort_labels:}
%   The main sorting function.  It does not receive arguments, but it is
%   expected to be run inside \cs{@@_zcref:nnnn} where a number of environment
%   variables are to be set appropriately.  In particular,
%   \cs{l_@@_zcref_labels_seq} should contain the labels received as argument
%   to \cs{zcref}, and the function performs its task by sorting this
%   variable.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_sort_labels:
  {
%    \end{macrocode}
% Store label types sequence.
%    \begin{macrocode}
    \seq_clear:N \l_@@_label_types_seq
    \tl_if_eq:NnF \l_@@_ref_propserty_tl { page }
      {
        \seq_map_function:NN \l_@@_zcref_labels_seq
          \@@_label_type_put_new_right:n
      }
%    \end{macrocode}
% Sort.
%    \begin{macrocode}
    \seq_sort:Nn \l_@@_zcref_labels_seq
      {
        \zref@ifrefundefined {##1}
          {
            \zref@ifrefundefined {##2}
              {
                % Neither label is defined.
                \sort_return_same:
              }
              {
                % The second label is defined, but the first isn't, leave the
                % undefined first (to be more visible).
                \sort_return_same:
              }
          }
          {
            \zref@ifrefundefined {##2}
              {
                % The first label is defined, but the second isn't, bring the
                % second forward.
                \sort_return_swapped:
              }
              {
                % The interesting case: both labels are defined.  References
                % to the "default" property or to the "page" are quite
                % different with regard to sorting, so we branch them here to
                % specialized functions.
                \tl_if_eq:NnTF \l_@@_ref_property_tl { page }
                  { \@@_sort_page:nn {##1} {##2} }
                  { \@@_sort_default:nn {##1} {##2} }
              }
          }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_label_type_put_new_right:n}
%   Auxiliary function used to store the order in which reference types appear
%   in the label list supplied by the user in \cs{zcref}.  It is expected to
%   be run inside \cs{@@_sort_labels:}, and stores the types sequence in
%   \cs{l_@@_label_types_seq}.  I have tried to handle the same task inside
%   \cs{seq_sort:Nn} in \cs{@@_sort_labels:} to spare mapping over
%   \cs{l_@@_zcref_labels_seq}, but it turned out it not to be easy to rely on
%   the order the labels get processed at that point, since the variable is
%   being sorted there.  Besides, the mapping is simple, not a particularly
%   expensive operation.  Anyway, this keeps things clean.
%   \begin{syntax}
%     \cs{@@_label_type_put_new_right:n} \Arg{label}
%   \end{syntax}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_label_type_put_new_right:n #1
  {
    \@@_extract_default:Nnnn
      \l_@@_label_type_a_tl {#1} { zc@type } { }
    \seq_if_in:NVF \l_@@_label_types_seq
      \l_@@_label_type_a_tl
      {
        \seq_put_right:NV \l_@@_label_types_seq
          \l_@@_label_type_a_tl
      }
  }
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}{\@@_sort_default:nn}
%   The heavy-lifting function for sorting of defined labels for ``default''
%   references (that is, a standard reference, not to ``page'').  This
%   function is expected to be called within the sorting loop of
%   \cs{@@_sort_labels:} and receives the pair of labels being considered for
%   a change of order or not.  It should \emph{always} ``return'' either
%   \cs{sort_return_same:} or \cs{sort_return_swapped:}.
%   \begin{syntax}
%     \cs{@@_sort_default:nn} \Arg{label a} \Arg{label b}
%   \end{syntax}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_sort_default:nn #1#2
  {
    \@@_extract_default:Nnnn
      \l_@@_label_type_a_tl {#1} { zc@type } { zc@missingtype }
    \@@_extract_default:Nnnn
      \l_@@_label_type_b_tl {#2} { zc@type } { zc@missingtype }

    \tl_if_eq:NNTF
      \l_@@_label_type_a_tl
      \l_@@_label_type_b_tl
      { \@@_sort_default_same_type:nn {#1} {#2} }
      { \@@_sort_default_different_types:nn {#1} {#2} }
  }
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}{\@@_sort_default_same_type:nn}
%   \begin{syntax}
%     \cs{@@_sort_default_same_type:nn} \Arg{label a} \Arg{label b}
%   \end{syntax}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_sort_default_same_type:nn #1#2
  {
    \@@_extract_default:Nnnn \l_@@_label_enclval_a_tl
      {#1} { zc@enclval } { }
    \tl_reverse:N \l_@@_label_enclval_a_tl
    \@@_extract_default:Nnnn \l_@@_label_enclval_b_tl
      {#2} { zc@enclval } { }
    \tl_reverse:N \l_@@_label_enclval_b_tl
    \@@_extract_default:Nnnn \l_@@_label_extdoc_a_tl
      {#1} { externaldocument } { }
    \@@_extract_default:Nnnn \l_@@_label_extdoc_b_tl
      {#2} { externaldocument } { }

    \bool_set_false:N \l_@@_sort_decided_bool

    % First we check if there's any "external document" difference (coming
    % from `zref-xr') and, if so, sort based on that.
    \tl_if_eq:NNF
      \l_@@_label_extdoc_a_tl
      \l_@@_label_extdoc_b_tl
      {
        \bool_if:nTF
          {
            \tl_if_empty_p:V \l_@@_label_extdoc_a_tl &&
            ! \tl_if_empty_p:V \l_@@_label_extdoc_b_tl
          }
          {
            \bool_set_true:N \l_@@_sort_decided_bool
            \sort_return_same:
          }
          {
            \bool_if:nTF
              {
                ! \tl_if_empty_p:V \l_@@_label_extdoc_a_tl &&
                \tl_if_empty_p:V \l_@@_label_extdoc_b_tl
              }
              {
                \bool_set_true:N \l_@@_sort_decided_bool
                \sort_return_swapped:
              }
              {
                \bool_set_true:N \l_@@_sort_decided_bool
                % Two different "external documents": last resort, sort by the
                % document name itself.
                \str_compare:eNeTF
                  { \l_@@_label_extdoc_b_tl } <
                  { \l_@@_label_extdoc_a_tl }
                  { \sort_return_swapped: }
                  { \sort_return_same:    }
              }
          }
      }

    \bool_until_do:Nn \l_@@_sort_decided_bool
      {
        \bool_if:nTF
          {
            % Both are empty: neither label has any (further) "enclosing
            % counters" (left).
            \tl_if_empty_p:V \l_@@_label_enclval_a_tl &&
            \tl_if_empty_p:V \l_@@_label_enclval_b_tl
          }
          {
            \bool_set_true:N \l_@@_sort_decided_bool
            \int_compare:nNnTF
              { \@@_extract:nnn {#1} { zc@cntval } { -1 } }
                >
              { \@@_extract:nnn {#2} { zc@cntval } { -1 } }
              { \sort_return_swapped: }
              { \sort_return_same:    }
          }
          {
            \bool_if:nTF
              {
                % `a' is empty (and `b' is not): `b' may be nested in `a'.
                \tl_if_empty_p:V \l_@@_label_enclval_a_tl
              }
              {
                \bool_set_true:N \l_@@_sort_decided_bool
                \int_compare:nNnTF
                  { \@@_extract:nnn {#1} { zc@cntval } { } }
                    >
                  { \tl_head:N \l_@@_label_enclval_b_tl }
                  { \sort_return_swapped: }
                  { \sort_return_same:    }
              }
              {
                \bool_if:nTF
                  {
                    % `b' is empty (and `a' is not): `a' may be nested in `b'.
                    \tl_if_empty_p:V \l_@@_label_enclval_b_tl
                  }
                  {
                    \bool_set_true:N \l_@@_sort_decided_bool
                    \int_compare:nNnTF
                      { \tl_head:N \l_@@_label_enclval_a_tl }
                        <
                      { \@@_extract:nnn {#2} { zc@cntval } { } }
                      { \sort_return_same:    }
                      { \sort_return_swapped: }
                  }
                  {
                    % Neither is empty: we can compare the values of the
                    % current enclosing counter in the loop, if they are
                    % equal, we are still in the loop, if they are not, a
                    % sorting decision can be made directly.
                    \int_compare:nNnTF
                      { \tl_head:N \l_@@_label_enclval_a_tl }
                        =
                      { \tl_head:N \l_@@_label_enclval_b_tl }
                      {
                        \tl_set:Nx \l_@@_label_enclval_a_tl
                          { \tl_tail:N \l_@@_label_enclval_a_tl }
                        \tl_set:Nx \l_@@_label_enclval_b_tl
                          { \tl_tail:N \l_@@_label_enclval_b_tl }
                      }
                      {
                        \bool_set_true:N \l_@@_sort_decided_bool
                        \int_compare:nNnTF
                          { \tl_head:N \l_@@_label_enclval_a_tl }
                            >
                          { \tl_head:N \l_@@_label_enclval_b_tl }
                          { \sort_return_swapped: }
                          { \sort_return_same:    }
                      }
                  }
              }
          }
      }
  }
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}{\@@_sort_default_different_types:nn}
%   \begin{syntax}
%     \cs{@@_sort_default_different_types:nn} \Arg{label a} \Arg{label b}
%   \end{syntax}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_sort_default_different_types:nn #1#2
  {
%    \end{macrocode}
% Retrieve sort priorities for \meta{label a} and \meta{label b}.
% \cs{l_@@_typesort_seq} was stored in reverse sequence, and we compute the
% sort priorities in the negative range, so that we can implicitly rely on `0'
% being the ``last value''.
%    \begin{macrocode}
    \int_zero:N \l_@@_sort_prior_a_int
    \int_zero:N \l_@@_sort_prior_b_int
    \seq_map_indexed_inline:Nn \l_@@_typesort_seq
      {
        \tl_if_eq:nnTF {##2} {{othertypes}}
          {
            \int_compare:nNnT { \l_@@_sort_prior_a_int } = { 0 }
              { \int_set:Nn \l_@@_sort_prior_a_int { - ##1 } }
            \int_compare:nNnT { \l_@@_sort_prior_b_int } = { 0 }
              { \int_set:Nn \l_@@_sort_prior_b_int { - ##1 } }
          }
          {
            \tl_if_eq:NnTF \l_@@_label_type_a_tl {##2}
              { \int_set:Nn \l_@@_sort_prior_a_int { - ##1 } }
              {
                \tl_if_eq:NnT \l_@@_label_type_b_tl {##2}
                  { \int_set:Nn \l_@@_sort_prior_b_int { - ##1 } }
              }
          }
      }
%    \end{macrocode}
% Then do the actual sorting.
%    \begin{macrocode}
    \bool_if:nTF
      {
        \int_compare_p:nNn
          { \l_@@_sort_prior_a_int } <
          { \l_@@_sort_prior_b_int }
      }
      { \sort_return_same: }
      {
        \bool_if:nTF
          {
            \int_compare_p:nNn
              { \l_@@_sort_prior_a_int } >
              { \l_@@_sort_prior_b_int }
          }
          { \sort_return_swapped: }
          {
            % Sort priorities are equal: the type that occurs first in
            % `labels', as given by the user, is kept (or brought) forward.
            \seq_map_inline:Nn \l_@@_label_types_seq
              {
                \tl_if_eq:NnTF \l_@@_label_type_a_tl {##1}
                  { \seq_map_break:n { \sort_return_same: } }
                  {
                    \tl_if_eq:NnT \l_@@_label_type_b_tl {##1}
                      { \seq_map_break:n { \sort_return_swapped: } }
                  }
              }
          }
      }
  }
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}{\@@_sort_page:nn}
%   The sorting function for sorting of defined labels for references to
%   ``page''.  This function is expected to be called within the sorting loop
%   of \cs{@@_sort_labels:} and receives the pair of labels being considered
%   for a change of order or not.  It should \emph{always} ``return'' either
%   \cs{sort_return_same:} or \cs{sort_return_swapped:}.  Compared to the
%   sorting of default labels, this is a piece of cake (thanks to
%   \texttt{abspage}).
%   \begin{syntax}
%     \cs{@@_sort_page:nn} \Arg{label a} \Arg{label b}
%   \end{syntax}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_sort_page:nn #1#2
  {
    \int_compare:nNnTF
      { \@@_extract:nnn {#1} { abspage } { -1 } }
        >
      { \@@_extract:nnn {#2} { abspage } { -1 } }
      { \sort_return_swapped: }
      { \sort_return_same:    }
  }
%    \end{macrocode}
% \end{macro}
%
%
%
% \section{Typesetting}
%
% ``Typesetting'' the reference, which here includes the parsing of the labels
% and eventual compression of labels in sequence into ranges, is definitely
% the ``crux'' of \pkg{zref-clever}.  This because we process the label set as
% a stack, in a single pass, and hence ``parsing'', ``compressing'', and
% ``typesetting'' must be decided upon at the same time, making it difficult
% to slice the job into more specific and self-contained tasks.  So, do bear
% this in mind before you curse me for the length of some of the functions
% below, or before a more orthodox ``docstripper'' complains about me not
% sticking to code commenting conventions to keep the code more readable in
% the \file{.dtx} file.
%
% While processing the label stack (kept in \cs{l_@@_typeset_labels_seq}),
% \cs{@@_typeset_refs:} ``sees'' two labels, and two labels only, the
% ``current'' one (kept in \cs{l_@@_label_a_tl}), and the ``next'' one (kept
% in \cs{l_@@_label_b_tl}).  However, the typesetting needs (a lot) more
% information than just these two immediate labels to make a number of
% critical decisions.  Some examples: i) We cannot know if labels ``current''
% and ``next'' of the same type are a ``pair'', or just ``elements in a
% list'', until we examine the label after ``next''; ii) If the ``next'' label
% is of the same type as the ``current'', and it is in immediate sequence to
% it, it potentially forms a ``range'', but we cannot know if ``next'' is
% actually the end of the range until we examined an arbitrary number of
% labels, and found one which is not in sequence from the previous one; iii)
% When processing a type block, the ``name'' comes first, however, we only
% know if that name should be plural, or if it should be included in the
% hyperlink, after processing an arbitrary number of labels and find one of a
% different type.  One could naively assume that just examining ``next'' would
% be enough for this, since we can know if it is of the same type or not.
% Alas, ``there be ranges'', and a compression operation may boil down to a
% single element, so we have to process the whole type block to know how its
% name should be typeset; iv) Similar issues apply to lists of type blocks,
% each of which is of arbitrary length: we can only know if two type blocks
% form a ``pair'' or are ``elements in a list'' when we finish the
% block. Etc.\ etc.\ etc.
%
% We handle this by storing the reference ``pieces'' in ``queues'', instead of
% typesetting them immediately upon processing.  The ``queues'' get typeset at
% the point where all the information needed is available, which usually
% happens when a type block finishes (we see something of a different type in
% ``next'', signaled by \cs{l_@@_last_of_type_bool}), or the stack itself
% finishes (has no more elements, signaled by \cs{l_@@_typeset_last_bool}).
% And, in processing a type block, the type ``name'' gets added last (on the
% left) of the queue.  The very first reference of its type always follows the
% name, since it may form a hyperlink with it (so we keep it stored
% separately, in \cs{l_@@_type_first_label_tl}, with
% \cs{l_@@_type_first_label_type_tl} being its type).  And, since we may need
% up to two type blocks in storage before typesetting, we have two of these
% ``queues'': \cs{l_@@_typeset_queue_curr_tl} and
% \cs{l_@@_typeset_queue_prev_tl}.
%
% Some of the relevant cases (e.g., distinguishing ``pair'' from ``list'') are
% handled by counters, the main ones are: one for the ``type''
% (\cs{l_@@_type_count_int}) and one for the ``label in the current type
% block'' (\cs{l_@@_label_count_int}).
%
% Range compression, in particular, relies heavily on counting to be able do
% distinguish relevant cases.  \cs{l_@@_range_count_int} counts the number of
% elements in the current sequential ``streak'', and
% \cs{l_@@_range_same_count_int} counts the number of \emph{equal} elements in
% that same ``streak''.  The difference between the two allows us to
% distinguish the cases in which a range actually ``skips'' a number in the
% sequence, in which case we should use a range separator, from when they are
% after all just contiguous, in which case a pair separator is called for.
% Since, as usual, we can only know this when a arbitrary long ``streak''
% finishes, we have to store the label which (potentially) begins a range
% (kept in \cs{l_@@_range_beg_label_tl}).  \cs{l_@@_next_maybe_range_bool}
% signals when ``next'' is potentially a range with ``current'', and
% \cs{l_@@_next_is_same_bool} when their values are actually equal.
%
%
% One further thing to discuss here -- to keep this ``on record'' -- is
% inhibition of compression for individual labels.  It is not difficult to
% handle it at the infrastructure side, what gets sloppy is the user facing
% syntax to signal such inhibition.  For some possible alternatives for this,
% suggested by \contributor{Enrico Gregorio}, \contributor{Phelype Oleinik},
% and \contributor{Steven B.\ Segletes} (and good ones at that) see
% \url{https://tex.stackexchange.com/q/611370}.  Yet another alternative would
% be an option receiving the label(s) not to be compressed, this would be a
% repetition, but would keep the syntax clean.  All in all, probably the best
% is simply not to allow individual inhibition of compression.  We can already
% control compression of each \cs{zcref} call with existing options, this
% should be enough.  I don't think the small extra flexibility individual
% label control for this would grant is worth the syntax disruption it would
% entail.  Anyway, it would be easy to deal with this in case the need arose,
% by just adding another condition (coming from whatever the chosen syntax
% was) when we check for \cs{@@_labels_in_sequence:nn} in
% \cs{@@_typeset_refs_not_last_of_type:}.  But I remain unconvinced of the
% pertinence of doing so.
%
%
% \subsection*{Variables}
%
% \begin{macro}
%   {
%     \l_@@_typeset_labels_seq ,
%     \l_@@_typeset_last_bool ,
%     \l_@@_last_of_type_bool ,
%   }
%   Auxiliary variables for \cs{@@_typeset_refs:} main stack control.
%    \begin{macrocode}
\seq_new:N \l_@@_typeset_labels_seq
\bool_new:N \l_@@_typeset_last_bool
\bool_new:N \l_@@_last_of_type_bool
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}
%   {
%     \l_@@_type_count_int ,
%     \l_@@_label_count_int ,
%     \l_@@_ref_count_int ,
%   }
%   Auxiliary variables for \cs{@@_typeset_refs:} main counters.
%    \begin{macrocode}
\int_new:N \l_@@_type_count_int
\int_new:N \l_@@_label_count_int
\int_new:N \l_@@_ref_count_int
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}
%   {
%     \l_@@_label_a_tl ,
%     \l_@@_label_b_tl ,
%     \l_@@_typeset_queue_prev_tl ,
%     \l_@@_typeset_queue_curr_tl ,
%     \l_@@_type_first_label_tl ,
%     \l_@@_type_first_label_type_tl
%   }
%   Auxiliary variables for \cs{@@_typeset_refs:} main ``queue'' control and
%   storage.
%    \begin{macrocode}
\tl_new:N \l_@@_label_a_tl
\tl_new:N \l_@@_label_b_tl
\tl_new:N \l_@@_typeset_queue_prev_tl
\tl_new:N \l_@@_typeset_queue_curr_tl
\tl_new:N \l_@@_type_first_label_tl
\tl_new:N \l_@@_type_first_label_type_tl
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}
%   {
%     \l_@@_type_name_tl ,
%     \l_@@_name_in_link_bool ,
%     \l_@@_type_name_missing_bool ,
%     \l_@@_name_format_tl ,
%     \l_@@_name_format_fallback_tl ,
%     \l_@@_type_name_gender_seq ,
%   }
%   Auxiliary variables for \cs{@@_typeset_refs:} type name handling.
%    \begin{macrocode}
\tl_new:N \l_@@_type_name_tl
\bool_new:N \l_@@_name_in_link_bool
\bool_new:N \l_@@_type_name_missing_bool
\tl_new:N \l_@@_name_format_tl
\tl_new:N \l_@@_name_format_fallback_tl
\seq_new:N \l_@@_type_name_gender_seq
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}
%   {
%     \l_@@_range_count_int ,
%     \l_@@_range_same_count_int ,
%     \l_@@_range_beg_label_tl ,
%     \l_@@_range_beg_is_first_bool ,
%     \l_@@_range_end_ref_tl ,
%     \l_@@_next_maybe_range_bool ,
%     \l_@@_next_is_same_bool ,
%   }
%   Auxiliary variables for \cs{@@_typeset_refs:} range handling.
%    \begin{macrocode}
\int_new:N \l_@@_range_count_int
\int_new:N \l_@@_range_same_count_int
\tl_new:N \l_@@_range_beg_label_tl
\bool_new:N \l_@@_range_beg_is_first_bool
\tl_new:N \l_@@_range_end_ref_tl
\bool_new:N \l_@@_next_maybe_range_bool
\bool_new:N \l_@@_next_is_same_bool
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}
%   {
%     \l_@@_tpairsep_tl ,
%     \l_@@_tlistsep_tl ,
%     \l_@@_tlastsep_tl ,
%     \l_@@_namesep_tl ,
%     \l_@@_pairsep_tl ,
%     \l_@@_listsep_tl ,
%     \l_@@_lastsep_tl ,
%     \l_@@_rangesep_tl ,
%     \l_@@_namefont_tl ,
%     \l_@@_reffont_tl ,
%     \l_@@_endrangefunc_tl ,
%     \l_@@_endrangeprop_tl ,
%     \l_@@_cap_bool ,
%     \l_@@_abbrev_bool ,
%     \l_@@_rangetopair_bool ,
%   }
%     Auxiliary variables for \cs{@@_typeset_refs:} separators, and font and
%     other options.
%    \begin{macrocode}
\tl_new:N \l_@@_tpairsep_tl
\tl_new:N \l_@@_tlistsep_tl
\tl_new:N \l_@@_tlastsep_tl
\tl_new:N \l_@@_namesep_tl
\tl_new:N \l_@@_pairsep_tl
\tl_new:N \l_@@_listsep_tl
\tl_new:N \l_@@_lastsep_tl
\tl_new:N \l_@@_rangesep_tl
\tl_new:N \l_@@_namefont_tl
\tl_new:N \l_@@_reffont_tl
\tl_new:N \l_@@_endrangefunc_tl
\tl_new:N \l_@@_endrangeprop_tl
\bool_new:N \l_@@_cap_bool
\bool_new:N \l_@@_abbrev_bool
\bool_new:N \l_@@_rangetopair_bool
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}
%   {
%     \l_@@_refbounds_first_seq ,
%     \l_@@_refbounds_first_sg_seq ,
%     \l_@@_refbounds_first_pb_seq ,
%     \l_@@_refbounds_first_rb_seq ,
%     \l_@@_refbounds_mid_seq ,
%     \l_@@_refbounds_mid_rb_seq ,
%     \l_@@_refbounds_mid_re_seq ,
%     \l_@@_refbounds_last_seq ,
%     \l_@@_refbounds_last_pe_seq ,
%     \l_@@_refbounds_last_re_seq ,
%     \l_@@_type_first_refbounds_seq ,
%     \l_@@_type_first_refbounds_set_bool ,
%   }
%   Auxiliary variables for \cs{@@_typeset_refs:}: advanced reference format
%   options.
%    \begin{macrocode}
\seq_new:N \l_@@_refbounds_first_seq
\seq_new:N \l_@@_refbounds_first_sg_seq
\seq_new:N \l_@@_refbounds_first_pb_seq
\seq_new:N \l_@@_refbounds_first_rb_seq
\seq_new:N \l_@@_refbounds_mid_seq
\seq_new:N \l_@@_refbounds_mid_rb_seq
\seq_new:N \l_@@_refbounds_mid_re_seq
\seq_new:N \l_@@_refbounds_last_seq
\seq_new:N \l_@@_refbounds_last_pe_seq
\seq_new:N \l_@@_refbounds_last_re_seq
\seq_new:N \l_@@_type_first_refbounds_seq
\bool_new:N \l_@@_type_first_refbounds_set_bool
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\l_@@_verbose_testing_bool}
%   Internal variable which enables extra log messaging at points of interest
%   in the code for purposes of regression testing.  Particularly relevant to
%   keep track of expansion control in \cs{l_@@_typeset_queue_curr_tl}.
%    \begin{macrocode}
\bool_new:N \l_@@_verbose_testing_bool
%    \end{macrocode}
% \end{macro}
%
%
% \subsection*{Main functions}
%
% \begin{macro}{\@@_typeset_refs:}
%   Main typesetting function for \cs{zcref}.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_typeset_refs:
  {
    \seq_set_eq:NN \l_@@_typeset_labels_seq
      \l_@@_zcref_labels_seq
    \tl_clear:N \l_@@_typeset_queue_prev_tl
    \tl_clear:N \l_@@_typeset_queue_curr_tl
    \tl_clear:N \l_@@_type_first_label_tl
    \tl_clear:N \l_@@_type_first_label_type_tl
    \tl_clear:N \l_@@_range_beg_label_tl
    \tl_clear:N \l_@@_range_end_ref_tl
    \int_zero:N \l_@@_label_count_int
    \int_zero:N \l_@@_type_count_int
    \int_zero:N \l_@@_ref_count_int
    \int_zero:N \l_@@_range_count_int
    \int_zero:N \l_@@_range_same_count_int
    \bool_set_false:N \l_@@_range_beg_is_first_bool
    \bool_set_false:N \l_@@_type_first_refbounds_set_bool

    % Get type block options (not type-specific).
    \@@_get_rf_opt_tl:nxxN { tpairsep }
      { \l_@@_label_type_a_tl }
      { \l_@@_ref_language_tl }
      \l_@@_tpairsep_tl
    \@@_get_rf_opt_tl:nxxN { tlistsep }
      { \l_@@_label_type_a_tl }
      { \l_@@_ref_language_tl }
      \l_@@_tlistsep_tl
    \@@_get_rf_opt_tl:nxxN { tlastsep }
      { \l_@@_label_type_a_tl }
      { \l_@@_ref_language_tl }
      \l_@@_tlastsep_tl

    % Process label stack.
    \bool_set_false:N \l_@@_typeset_last_bool
    \bool_until_do:Nn \l_@@_typeset_last_bool
      {
        \seq_pop_left:NN \l_@@_typeset_labels_seq
          \l_@@_label_a_tl
        \seq_if_empty:NTF \l_@@_typeset_labels_seq
          {
            \tl_clear:N \l_@@_label_b_tl
            \bool_set_true:N \l_@@_typeset_last_bool
          }
          {
            \seq_get_left:NN \l_@@_typeset_labels_seq
              \l_@@_label_b_tl
          }

        \tl_if_eq:NnTF \l_@@_ref_property_tl { page }
          {
            \tl_set:Nn \l_@@_label_type_a_tl { page }
            \tl_set:Nn \l_@@_label_type_b_tl { page }
          }
          {
            \@@_extract_default:NVnn
              \l_@@_label_type_a_tl
              \l_@@_label_a_tl { zc@type } { zc@missingtype }
            \@@_extract_default:NVnn
              \l_@@_label_type_b_tl
              \l_@@_label_b_tl { zc@type } { zc@missingtype }
          }

        % First, we establish whether the "current label" (i.e. `a') is the
        % last one of its type.  This can happen because the "next label"
        % (i.e. `b') is of a different type (or different definition status),
        % or because we are at the end of the list.
        \bool_if:NTF \l_@@_typeset_last_bool
          { \bool_set_true:N \l_@@_last_of_type_bool }
          {
            \zref@ifrefundefined { \l_@@_label_a_tl }
              {
                \zref@ifrefundefined { \l_@@_label_b_tl }
                  { \bool_set_false:N \l_@@_last_of_type_bool }
                  { \bool_set_true:N \l_@@_last_of_type_bool  }
              }
              {
                \zref@ifrefundefined { \l_@@_label_b_tl }
                  { \bool_set_true:N \l_@@_last_of_type_bool }
                  {
                    % Neither is undefined, we must check the types.
                    \tl_if_eq:NNTF
                      \l_@@_label_type_a_tl
                      \l_@@_label_type_b_tl
                      { \bool_set_false:N \l_@@_last_of_type_bool }
                      { \bool_set_true:N \l_@@_last_of_type_bool  }
                  }
              }
          }

        % Handle warnings in case of reference or type undefined.
        % Test: `zc-typeset01.lvt': "Typeset refs: warn ref undefined"
        \zref@refused { \l_@@_label_a_tl }
        % Test: `zc-typeset01.lvt': "Typeset refs: warn missing type"
        \zref@ifrefundefined { \l_@@_label_a_tl }
          {}
          {
            \tl_if_eq:NnT \l_@@_label_type_a_tl { zc@missingtype }
              {
                \msg_warning:nnx { zref-clever } { missing-type }
                  { \l_@@_label_a_tl }
              }
            \zref@ifrefcontainsprop
              { \l_@@_label_a_tl }
              { \l_@@_ref_property_tl }
              { }
              {
                \msg_warning:nnxx { zref-clever } { missing-property }
                  { \l_@@_ref_property_tl }
                  { \l_@@_label_a_tl }
              }
          }

        % Get possibly type-specific separators, refbounds, font and other
        % options, once per type.
        \int_compare:nNnT { \l_@@_label_count_int } = { 0 }
          {
            \@@_get_rf_opt_tl:nxxN { namesep }
              { \l_@@_label_type_a_tl }
              { \l_@@_ref_language_tl }
              \l_@@_namesep_tl
            \@@_get_rf_opt_tl:nxxN { pairsep }
              { \l_@@_label_type_a_tl }
              { \l_@@_ref_language_tl }
              \l_@@_pairsep_tl
            \@@_get_rf_opt_tl:nxxN { listsep }
              { \l_@@_label_type_a_tl }
              { \l_@@_ref_language_tl }
              \l_@@_listsep_tl
            \@@_get_rf_opt_tl:nxxN { lastsep }
              { \l_@@_label_type_a_tl }
              { \l_@@_ref_language_tl }
              \l_@@_lastsep_tl
            \@@_get_rf_opt_tl:nxxN { rangesep }
              { \l_@@_label_type_a_tl }
              { \l_@@_ref_language_tl }
              \l_@@_rangesep_tl
            \@@_get_rf_opt_tl:nxxN { namefont }
              { \l_@@_label_type_a_tl }
              { \l_@@_ref_language_tl }
              \l_@@_namefont_tl
            \@@_get_rf_opt_tl:nxxN { reffont }
              { \l_@@_label_type_a_tl }
              { \l_@@_ref_language_tl }
              \l_@@_reffont_tl
            \@@_get_rf_opt_tl:nxxN { endrangefunc }
              { \l_@@_label_type_a_tl }
              { \l_@@_ref_language_tl }
              \l_@@_endrangefunc_tl
            \@@_get_rf_opt_tl:nxxN { endrangeprop }
              { \l_@@_label_type_a_tl }
              { \l_@@_ref_language_tl }
              \l_@@_endrangeprop_tl
            \@@_get_rf_opt_bool:nnxxN { cap } { false }
              { \l_@@_label_type_a_tl }
              { \l_@@_ref_language_tl }
              \l_@@_cap_bool
            \@@_get_rf_opt_bool:nnxxN { abbrev } { false }
              { \l_@@_label_type_a_tl }
              { \l_@@_ref_language_tl }
              \l_@@_abbrev_bool
            \@@_get_rf_opt_bool:nnxxN { rangetopair } { true }
              { \l_@@_label_type_a_tl }
              { \l_@@_ref_language_tl }
              \l_@@_rangetopair_bool
            \@@_get_rf_opt_seq:nxxN { refbounds-first }
              { \l_@@_label_type_a_tl }
              { \l_@@_ref_language_tl }
              \l_@@_refbounds_first_seq
            \@@_get_rf_opt_seq:nxxN { refbounds-first-sg }
              { \l_@@_label_type_a_tl }
              { \l_@@_ref_language_tl }
              \l_@@_refbounds_first_sg_seq
            \@@_get_rf_opt_seq:nxxN { refbounds-first-pb }
              { \l_@@_label_type_a_tl }
              { \l_@@_ref_language_tl }
              \l_@@_refbounds_first_pb_seq
            \@@_get_rf_opt_seq:nxxN { refbounds-first-rb }
              { \l_@@_label_type_a_tl }
              { \l_@@_ref_language_tl }
              \l_@@_refbounds_first_rb_seq
            \@@_get_rf_opt_seq:nxxN { refbounds-mid }
              { \l_@@_label_type_a_tl }
              { \l_@@_ref_language_tl }
              \l_@@_refbounds_mid_seq
            \@@_get_rf_opt_seq:nxxN { refbounds-mid-rb }
              { \l_@@_label_type_a_tl }
              { \l_@@_ref_language_tl }
              \l_@@_refbounds_mid_rb_seq
            \@@_get_rf_opt_seq:nxxN { refbounds-mid-re }
              { \l_@@_label_type_a_tl }
              { \l_@@_ref_language_tl }
              \l_@@_refbounds_mid_re_seq
            \@@_get_rf_opt_seq:nxxN { refbounds-last }
              { \l_@@_label_type_a_tl }
              { \l_@@_ref_language_tl }
              \l_@@_refbounds_last_seq
            \@@_get_rf_opt_seq:nxxN { refbounds-last-pe }
              { \l_@@_label_type_a_tl }
              { \l_@@_ref_language_tl }
              \l_@@_refbounds_last_pe_seq
            \@@_get_rf_opt_seq:nxxN { refbounds-last-re }
              { \l_@@_label_type_a_tl }
              { \l_@@_ref_language_tl }
              \l_@@_refbounds_last_re_seq
          }

        % Here we send this to a couple of auxiliary functions.
        \bool_if:NTF \l_@@_last_of_type_bool
          % There exists no next label of the same type as the current.
          { \@@_typeset_refs_last_of_type: }
          % There exists a next label of the same type as the current.
          { \@@_typeset_refs_not_last_of_type: }
      }
  }
%    \end{macrocode}
% \end{macro}
%
%
% This is actually the one meaningful ``big branching'' we can do while
% processing the label stack: i) the ``current'' label is the last of its type
% block; or ii) the ``current'' label is \emph{not} the last of its type
% block.  Indeed, as mentioned above, quite a number of things can only be
% decided when the type block ends, and we only know this when we look at the
% ``next'' label and find something of a different ``type'' (loose here, maybe
% different definition status, maybe end of stack).  So, though this is not
% very strict, \cs{@@_typeset_refs_last_of_type:} is more of a ``wrapping
% up'' function, and it is indeed the one which does the actual typesetting,
% while \cs{@@_typeset_refs_not_last_of_type:} is more of an
% ``accumulation'' function.
%
%
% \begin{macro}{\@@_typeset_refs_last_of_type:}
%   Handles typesetting when the current label is the last of its type.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_typeset_refs_last_of_type:
  {
    % Process the current label to the current queue.
    \int_case:nnF { \l_@@_label_count_int }
      {
        % It is the last label of its type, but also the first one, and that's
        % what matters here: just store it.
        % Test: `zc-typeset01.lvt': "Last of type: single"
        { 0 }
        {
          \tl_set:NV \l_@@_type_first_label_tl
            \l_@@_label_a_tl
          \tl_set:NV \l_@@_type_first_label_type_tl
            \l_@@_label_type_a_tl
          \seq_set_eq:NN \l_@@_type_first_refbounds_seq
            \l_@@_refbounds_first_sg_seq
          \bool_set_true:N \l_@@_type_first_refbounds_set_bool
        }

        % The last is the second: we have a pair (if not repeated).
        % Test: `zc-typeset01.lvt': "Last of type: pair"
        { 1 }
        {
          \int_compare:nNnTF { \l_@@_range_same_count_int } = { 1 }
            {
              \seq_set_eq:NN \l_@@_type_first_refbounds_seq
                \l_@@_refbounds_first_sg_seq
              \bool_set_true:N \l_@@_type_first_refbounds_set_bool
            }
            {
              \tl_put_right:Nx \l_@@_typeset_queue_curr_tl
                {
                  \exp_not:V \l_@@_pairsep_tl
                  \@@_get_ref:VN \l_@@_label_a_tl
                    \l_@@_refbounds_last_pe_seq
                }
              \seq_set_eq:NN \l_@@_type_first_refbounds_seq
                \l_@@_refbounds_first_pb_seq
              \bool_set_true:N \l_@@_type_first_refbounds_set_bool
            }
        }
      }
      % Last is third or more of its type: without repetition, we'd have the
      % last element on a list, but control for possible repetition.
      {
        \int_case:nnF { \l_@@_range_count_int }
          {
            % There was no range going on.
            % Test: `zc-typeset01.lvt': "Last of type: not range"
            { 0 }
            {
              \int_compare:nNnTF { \l_@@_ref_count_int } < { 2 }
                {
                  \tl_put_right:Nx \l_@@_typeset_queue_curr_tl
                    {
                      \exp_not:V \l_@@_pairsep_tl
                      \@@_get_ref:VN \l_@@_label_a_tl
                        \l_@@_refbounds_last_pe_seq
                    }
                }
                {
                  \tl_put_right:Nx \l_@@_typeset_queue_curr_tl
                    {
                      \exp_not:V \l_@@_lastsep_tl
                      \@@_get_ref:VN \l_@@_label_a_tl
                        \l_@@_refbounds_last_seq
                    }
                }
            }
            % Last in the range is also the second in it.
            % Test: `zc-typeset01.lvt': "Last of type: pair in sequence"
            { 1 }
            {
              \int_compare:nNnTF
                { \l_@@_range_same_count_int } = { 1 }
                {
                  % We know `range_beg_is_first_bool' is false, since this is
                  % the second element in the range, but the third or more in
                  % the type list.
                  \tl_put_right:Nx \l_@@_typeset_queue_curr_tl
                    {
                      \exp_not:V \l_@@_pairsep_tl
                      \@@_get_ref:VN
                        \l_@@_range_beg_label_tl
                        \l_@@_refbounds_last_pe_seq
                    }
                  \seq_set_eq:NN \l_@@_type_first_refbounds_seq
                    \l_@@_refbounds_first_pb_seq
                  \bool_set_true:N
                    \l_@@_type_first_refbounds_set_bool
                }
                {
                  \tl_put_right:Nx \l_@@_typeset_queue_curr_tl
                    {
                      \exp_not:V \l_@@_listsep_tl
                      \@@_get_ref:VN
                        \l_@@_range_beg_label_tl
                        \l_@@_refbounds_mid_seq
                      \exp_not:V \l_@@_lastsep_tl
                      \@@_get_ref:VN \l_@@_label_a_tl
                        \l_@@_refbounds_last_seq
                    }
                }
            }
          }
          % Last in the range is third or more in it.
          {
            \int_case:nnF
              {
                \l_@@_range_count_int -
                \l_@@_range_same_count_int
              }
              {
                % Repetition, not a range.
                % Test: `zc-typeset01.lvt': "Last of type: range to one"
                { 0 }
                {
                  % If `range_beg_is_first_bool' is true, it means it was also
                  % the first of the type, and hence its typesetting was
                  % already handled, and we just have to set refbounds.
                  \bool_if:NTF \l_@@_range_beg_is_first_bool
                    {
                      \seq_set_eq:NN \l_@@_type_first_refbounds_seq
                        \l_@@_refbounds_first_sg_seq
                      \bool_set_true:N
                        \l_@@_type_first_refbounds_set_bool
                    }
                    {
                      \int_compare:nNnTF
                        { \l_@@_ref_count_int } < { 2 }
                        {
                          \tl_put_right:Nx \l_@@_typeset_queue_curr_tl
                            {
                              \exp_not:V \l_@@_pairsep_tl
                              \@@_get_ref:VN
                                \l_@@_range_beg_label_tl
                                \l_@@_refbounds_last_pe_seq
                            }
                        }
                        {
                          \tl_put_right:Nx \l_@@_typeset_queue_curr_tl
                            {
                              \exp_not:V \l_@@_lastsep_tl
                              \@@_get_ref:VN
                                \l_@@_range_beg_label_tl
                                \l_@@_refbounds_last_seq
                            }
                        }
                    }
                }
                % A `range', but with no skipped value, treat as pair if range
                % started with first of type, otherwise as list.
                % Test: `zc-typeset01.lvt': "Last of type: range to pair"
                { 1 }
                {
                  % Ditto.
                  \bool_if:NTF \l_@@_range_beg_is_first_bool
                    {
                      \seq_set_eq:NN \l_@@_type_first_refbounds_seq
                        \l_@@_refbounds_first_pb_seq
                      \bool_set_true:N
                        \l_@@_type_first_refbounds_set_bool
                      \tl_put_right:Nx \l_@@_typeset_queue_curr_tl
                        {
                          \exp_not:V \l_@@_pairsep_tl
                          \@@_get_ref:VN \l_@@_label_a_tl
                            \l_@@_refbounds_last_pe_seq
                        }
                    }
                    {
                      \tl_put_right:Nx \l_@@_typeset_queue_curr_tl
                        {
                          \exp_not:V \l_@@_listsep_tl
                          \@@_get_ref:VN
                            \l_@@_range_beg_label_tl
                            \l_@@_refbounds_mid_seq
                        }
                      \tl_put_right:Nx \l_@@_typeset_queue_curr_tl
                        {
                          \exp_not:V \l_@@_lastsep_tl
                          \@@_get_ref:VN \l_@@_label_a_tl
                            \l_@@_refbounds_last_seq
                        }
                    }
                }
              }
              {
                % An actual range.
                % Test: `zc-typeset01.lvt': "Last of type: range"
                % Ditto.
                \bool_if:NTF \l_@@_range_beg_is_first_bool
                  {
                    \seq_set_eq:NN \l_@@_type_first_refbounds_seq
                      \l_@@_refbounds_first_rb_seq
                    \bool_set_true:N
                      \l_@@_type_first_refbounds_set_bool
                  }
                  {
                    \int_compare:nNnTF
                      { \l_@@_ref_count_int } < { 2 }
                      {
                        \tl_put_right:Nx \l_@@_typeset_queue_curr_tl
                          {
                            \exp_not:V \l_@@_pairsep_tl
                            \@@_get_ref:VN
                              \l_@@_range_beg_label_tl
                              \l_@@_refbounds_mid_rb_seq
                          }
                        \seq_set_eq:NN
                          \l_@@_type_first_refbounds_seq
                          \l_@@_refbounds_first_pb_seq
                        \bool_set_true:N
                          \l_@@_type_first_refbounds_set_bool
                      }
                      {
                        \tl_put_right:Nx \l_@@_typeset_queue_curr_tl
                          {
                            \exp_not:V \l_@@_lastsep_tl
                            \@@_get_ref:VN
                              \l_@@_range_beg_label_tl
                              \l_@@_refbounds_mid_rb_seq
                          }
                      }
                  }
                \bool_lazy_and:nnTF
                  { ! \tl_if_empty_p:N \l_@@_endrangefunc_tl }
                  { \cs_if_exist_p:c { \l_@@_endrangefunc_tl :VVN } }
                  {
                    \use:c { \l_@@_endrangefunc_tl :VVN }
                      \l_@@_range_beg_label_tl
                      \l_@@_label_a_tl
                      \l_@@_range_end_ref_tl
                    \tl_put_right:Nx \l_@@_typeset_queue_curr_tl
                      {
                        \exp_not:V \l_@@_rangesep_tl
                        \@@_get_ref_endrange:VVN
                          \l_@@_label_a_tl
                          \l_@@_range_end_ref_tl
                          \l_@@_refbounds_last_re_seq
                      }
                  }
                  {
                    \tl_put_right:Nx \l_@@_typeset_queue_curr_tl
                      {
                        \exp_not:V \l_@@_rangesep_tl
                        \@@_get_ref:VN \l_@@_label_a_tl
                          \l_@@_refbounds_last_re_seq
                      }
                  }
              }
          }
      }

    % Handle "range" option.  The idea is simple: if the queue is not empty,
    % we replace it with the end of the range (or pair).  We can still
    % retrieve the end of the range from `label_a' since we know to be
    % processing the last label of its type at this point.
    \bool_if:NT \l_@@_typeset_range_bool
      {
        \tl_if_empty:NTF \l_@@_typeset_queue_curr_tl
          {
            \zref@ifrefundefined { \l_@@_type_first_label_tl }
              { }
              {
                \msg_warning:nnx { zref-clever } { single-element-range }
                  { \l_@@_type_first_label_type_tl }
              }
          }
          {
            \bool_set_false:N \l_@@_next_maybe_range_bool
            \bool_if:NT \l_@@_rangetopair_bool
              {
                \zref@ifrefundefined { \l_@@_type_first_label_tl }
                  { }
                  {
                    \@@_labels_in_sequence:nn
                      { \l_@@_type_first_label_tl }
                      { \l_@@_label_a_tl }
                  }
              }
            % Test: `zc-typeset01.lvt': "Last of type: option range"
            % Test: `zc-typeset01.lvt': "Last of type: option range to pair"
            \bool_if:NTF \l_@@_next_maybe_range_bool
              {
                \tl_set:Nx \l_@@_typeset_queue_curr_tl
                  {
                    \exp_not:V \l_@@_pairsep_tl
                    \@@_get_ref:VN \l_@@_label_a_tl
                      \l_@@_refbounds_last_pe_seq
                  }
                \seq_set_eq:NN \l_@@_type_first_refbounds_seq
                  \l_@@_refbounds_first_pb_seq
                \bool_set_true:N \l_@@_type_first_refbounds_set_bool
              }
              {
                \bool_lazy_and:nnTF
                  { ! \tl_if_empty_p:N \l_@@_endrangefunc_tl }
                  { \cs_if_exist_p:c { \l_@@_endrangefunc_tl :VVN } }
                  {
                    % We must get `type_first_label_tl' instead of
                    % `range_beg_label_tl' here, since it is not necessary
                    % that the first of type was actually starting a range for
                    % the `range' option to be used.
                    \use:c { \l_@@_endrangefunc_tl :VVN }
                      \l_@@_type_first_label_tl
                      \l_@@_label_a_tl
                      \l_@@_range_end_ref_tl
                    \tl_set:Nx \l_@@_typeset_queue_curr_tl
                      {
                        \exp_not:V \l_@@_rangesep_tl
                        \@@_get_ref_endrange:VVN
                          \l_@@_label_a_tl
                          \l_@@_range_end_ref_tl
                          \l_@@_refbounds_last_re_seq
                      }
                  }
                  {
                    \tl_set:Nx \l_@@_typeset_queue_curr_tl
                      {
                        \exp_not:V \l_@@_rangesep_tl
                        \@@_get_ref:VN \l_@@_label_a_tl
                          \l_@@_refbounds_last_re_seq
                      }
                  }
                \seq_set_eq:NN \l_@@_type_first_refbounds_seq
                  \l_@@_refbounds_first_rb_seq
                \bool_set_true:N \l_@@_type_first_refbounds_set_bool
              }
          }
      }

    % If none of the special cases for the first of type refbounds have been
    % set, do it.
    \bool_if:NF \l_@@_type_first_refbounds_set_bool
      {
        \seq_set_eq:NN \l_@@_type_first_refbounds_seq
          \l_@@_refbounds_first_seq
      }

    % Now that the type block is finished, we can add the name and the first
    % ref to the queue.  Also, if "typeset" option is not "both", handle it
    % here as well.
    \@@_type_name_setup:
    \bool_if:nTF
      { \l_@@_typeset_ref_bool && \l_@@_typeset_name_bool }
      {
        \tl_put_left:Nx \l_@@_typeset_queue_curr_tl
          { \@@_get_ref_first: }
      }
      {
        \bool_if:NTF \l_@@_typeset_ref_bool
          {
            % Test: `zc-typeset01.lvt': "Last of type: option typeset ref"
            \tl_put_left:Nx \l_@@_typeset_queue_curr_tl
              {
                \@@_get_ref:VN \l_@@_type_first_label_tl
                  \l_@@_type_first_refbounds_seq
              }
          }
          {
            \bool_if:NTF \l_@@_typeset_name_bool
              {
                % Test: `zc-typeset01.lvt': "Last of type: option typeset name"
                \tl_set:Nx \l_@@_typeset_queue_curr_tl
                  {
                    \bool_if:NTF \l_@@_name_in_link_bool
                      {
                        \exp_not:N \group_begin:
                        \exp_not:V \l_@@_namefont_tl
                        \@@_hyperlink:nnn
                          {
                            \@@_extract_url_unexp:V
                              \l_@@_type_first_label_tl
                          }
                          {
                            \@@_extract_unexp:Vnn
                              \l_@@_type_first_label_tl
                              { anchor } { }
                          }
                          { \exp_not:V \l_@@_type_name_tl }
                        \exp_not:N \group_end:
                      }
                      {
                        \exp_not:N \group_begin:
                        \exp_not:V \l_@@_namefont_tl
                        \exp_not:V \l_@@_type_name_tl
                        \exp_not:N \group_end:
                      }
                  }
              }
              {
                % Logically, this case would correspond to "typeset=none", but
                % it should not occur, given that the options are set up to
                % typeset either "ref" or "name".  Still, leave here a
                % sensible fallback, equal to the behavior of "both".
                % Test: `zc-typeset01.lvt': "Last of type: option typeset none"
                \tl_put_left:Nx \l_@@_typeset_queue_curr_tl
                  { \@@_get_ref_first: }
              }
          }
      }

    % Typeset the previous type block, if there is one.
    \int_compare:nNnT { \l_@@_type_count_int } > { 0 }
      {
        \int_compare:nNnT { \l_@@_type_count_int } > { 1 }
          { \l_@@_tlistsep_tl }
        \l_@@_typeset_queue_prev_tl
      }

    % Extra log for testing.
    \bool_if:NT \l_@@_verbose_testing_bool
      { \tl_show:N \l_@@_typeset_queue_curr_tl }

    % Wrap up loop, or prepare for next iteration.
    \bool_if:NTF \l_@@_typeset_last_bool
      {
        % We are finishing, typeset the current queue.
        \int_case:nnF { \l_@@_type_count_int }
          {
            % Single type.
            % Test: `zc-typeset01.lvt': "Last of type: single type"
            { 0 }
            { \l_@@_typeset_queue_curr_tl }
            % Pair of types.
            % Test: `zc-typeset01.lvt': "Last of type: pair of types"
            { 1 }
            {
              \l_@@_tpairsep_tl
              \l_@@_typeset_queue_curr_tl
            }
          }
          {
            % Last in list of types.
            % Test: `zc-typeset01.lvt': "Last of type: list of types"
            \l_@@_tlastsep_tl
            \l_@@_typeset_queue_curr_tl
          }
        % And nudge in case of multitype reference.
        \bool_lazy_all:nT
          {
            { \l_@@_nudge_enabled_bool }
            { \l_@@_nudge_multitype_bool }
            { \int_compare_p:nNn { \l_@@_type_count_int } > { 0 } }
          }
          { \msg_warning:nn { zref-clever } { nudge-multitype } }
      }
      {
        % There are further labels, set variables for next iteration.
        \tl_set_eq:NN \l_@@_typeset_queue_prev_tl
          \l_@@_typeset_queue_curr_tl
        \tl_clear:N \l_@@_typeset_queue_curr_tl
        \tl_clear:N \l_@@_type_first_label_tl
        \tl_clear:N \l_@@_type_first_label_type_tl
        \tl_clear:N \l_@@_range_beg_label_tl
        \tl_clear:N \l_@@_range_end_ref_tl
        \int_zero:N \l_@@_label_count_int
        \int_zero:N \l_@@_ref_count_int
        \int_incr:N \l_@@_type_count_int
        \int_zero:N \l_@@_range_count_int
        \int_zero:N \l_@@_range_same_count_int
        \bool_set_false:N \l_@@_range_beg_is_first_bool
        \bool_set_false:N \l_@@_type_first_refbounds_set_bool
      }
  }
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}{\@@_typeset_refs_not_last_of_type:}
%   Handles typesetting when the current label is not the last of its type.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_typeset_refs_not_last_of_type:
  {
    % Signal if next label may form a range with the current one (only
    % considered if compression is enabled in the first place).
    \bool_set_false:N \l_@@_next_maybe_range_bool
    \bool_set_false:N \l_@@_next_is_same_bool
    \bool_if:NT \l_@@_typeset_compress_bool
      {
        \zref@ifrefundefined { \l_@@_label_a_tl }
          { }
          {
            \@@_labels_in_sequence:nn
              { \l_@@_label_a_tl } { \l_@@_label_b_tl }
          }
      }

    % Process the current label to the current queue.
    \int_compare:nNnTF { \l_@@_label_count_int } = { 0 }
      {
        % Current label is the first of its type (also not the last, but it
        % doesn't matter here): just store the label.
        \tl_set:NV \l_@@_type_first_label_tl
          \l_@@_label_a_tl
        \tl_set:NV \l_@@_type_first_label_type_tl
          \l_@@_label_type_a_tl
        \int_incr:N \l_@@_ref_count_int

        % If the next label may be part of a range, signal it (we deal with it
        % as the "first", and must do it there, to handle hyperlinking), but
        % also step the range counters.
        % Test: `zc-typeset01.lvt': "Not last of type: first is range"
        \bool_if:NT \l_@@_next_maybe_range_bool
          {
            \bool_set_true:N \l_@@_range_beg_is_first_bool
            \tl_set:NV \l_@@_range_beg_label_tl
              \l_@@_label_a_tl
            \tl_clear:N \l_@@_range_end_ref_tl
            \int_incr:N \l_@@_range_count_int
            \bool_if:NT \l_@@_next_is_same_bool
              { \int_incr:N \l_@@_range_same_count_int }
          }
      }
      {
        % Current label is neither the first (nor the last) of its type.
        \bool_if:NTF \l_@@_next_maybe_range_bool
          {
            % Starting, or continuing a range.
            \int_compare:nNnTF
              { \l_@@_range_count_int } = { 0 }
              {
                % There was no range going, we are starting one.
                \tl_set:NV \l_@@_range_beg_label_tl
                  \l_@@_label_a_tl
                \tl_clear:N \l_@@_range_end_ref_tl
                \int_incr:N \l_@@_range_count_int
                \bool_if:NT \l_@@_next_is_same_bool
                  { \int_incr:N \l_@@_range_same_count_int }
              }
              {
                % Second or more in the range, but not the last.
                \int_incr:N \l_@@_range_count_int
                \bool_if:NT \l_@@_next_is_same_bool
                  { \int_incr:N \l_@@_range_same_count_int }
              }
          }
          {
            % Next element is not in sequence: there was no range, or we are
            % closing one.
            \int_case:nnF { \l_@@_range_count_int }
              {
                % There was no range going on.
                % Test: `zc-typeset01.lvt': "Not last of type: no range"
                { 0 }
                {
                  \int_incr:N \l_@@_ref_count_int
                  \tl_put_right:Nx \l_@@_typeset_queue_curr_tl
                    {
                      \exp_not:V \l_@@_listsep_tl
                      \@@_get_ref:VN \l_@@_label_a_tl
                        \l_@@_refbounds_mid_seq
                    }
                }
                % Last is second in the range: if `range_same_count' is also
                % `1', it's a repetition (drop it), otherwise, it's a "pair
                % within a list", treat as list.
                % Test: `zc-typeset01.lvt': "Not last of type: range pair to one"
                % Test: `zc-typeset01.lvt': "Not last of type: range pair"
                { 1 }
                {
                  \bool_if:NTF \l_@@_range_beg_is_first_bool
                    {
                      \seq_set_eq:NN \l_@@_type_first_refbounds_seq
                        \l_@@_refbounds_first_seq
                      \bool_set_true:N
                        \l_@@_type_first_refbounds_set_bool
                    }
                    {
                      \int_incr:N \l_@@_ref_count_int
                      \tl_put_right:Nx \l_@@_typeset_queue_curr_tl
                        {
                          \exp_not:V \l_@@_listsep_tl
                          \@@_get_ref:VN
                            \l_@@_range_beg_label_tl
                            \l_@@_refbounds_mid_seq
                        }
                    }
                  \int_compare:nNnF
                    { \l_@@_range_same_count_int } = { 1 }
                    {
                      \int_incr:N \l_@@_ref_count_int
                      \tl_put_right:Nx \l_@@_typeset_queue_curr_tl
                        {
                          \exp_not:V \l_@@_listsep_tl
                          \@@_get_ref:VN
                            \l_@@_label_a_tl
                            \l_@@_refbounds_mid_seq
                        }
                    }
                }
              }
              {
                % Last is third or more in the range: if `range_count' and
                % `range_same_count' are the same, its a repetition (drop it),
                % if they differ by `1', its a list, if they differ by more,
                % it is a real range.
                \int_case:nnF
                  {
                    \l_@@_range_count_int -
                    \l_@@_range_same_count_int
                  }
                  {
                    % Test: `zc-typeset01.lvt': "Not last of type: range to one"
                    { 0 }
                    {
                      \bool_if:NTF \l_@@_range_beg_is_first_bool
                        {
                          \seq_set_eq:NN
                            \l_@@_type_first_refbounds_seq
                            \l_@@_refbounds_first_seq
                          \bool_set_true:N
                            \l_@@_type_first_refbounds_set_bool
                        }
                        {
                          \int_incr:N \l_@@_ref_count_int
                          \tl_put_right:Nx \l_@@_typeset_queue_curr_tl
                            {
                              \exp_not:V \l_@@_listsep_tl
                              \@@_get_ref:VN
                                \l_@@_range_beg_label_tl
                                \l_@@_refbounds_mid_seq
                            }
                        }
                    }
                    % Test: `zc-typeset01.lvt': "Not last of type: range to pair"
                    { 1 }
                    {
                      \bool_if:NTF \l_@@_range_beg_is_first_bool
                        {
                          \seq_set_eq:NN
                            \l_@@_type_first_refbounds_seq
                            \l_@@_refbounds_first_seq
                          \bool_set_true:N
                            \l_@@_type_first_refbounds_set_bool
                        }
                        {
                          \int_incr:N \l_@@_ref_count_int
                          \tl_put_right:Nx \l_@@_typeset_queue_curr_tl
                            {
                              \exp_not:V \l_@@_listsep_tl
                              \@@_get_ref:VN
                                \l_@@_range_beg_label_tl
                                \l_@@_refbounds_mid_seq
                            }
                        }
                      \int_incr:N \l_@@_ref_count_int
                      \tl_put_right:Nx \l_@@_typeset_queue_curr_tl
                        {
                          \exp_not:V \l_@@_listsep_tl
                          \@@_get_ref:VN \l_@@_label_a_tl
                            \l_@@_refbounds_mid_seq
                        }
                    }
                  }
                  {
                    % Test: `zc-typeset01.lvt': "Not last of type: range"
                    \bool_if:NTF \l_@@_range_beg_is_first_bool
                      {
                        \seq_set_eq:NN
                          \l_@@_type_first_refbounds_seq
                          \l_@@_refbounds_first_rb_seq
                        \bool_set_true:N
                          \l_@@_type_first_refbounds_set_bool
                      }
                      {
                        \int_incr:N \l_@@_ref_count_int
                        \tl_put_right:Nx \l_@@_typeset_queue_curr_tl
                          {
                            \exp_not:V \l_@@_listsep_tl
                            \@@_get_ref:VN
                              \l_@@_range_beg_label_tl
                              \l_@@_refbounds_mid_rb_seq
                          }
                      }
                    % For the purposes of the serial comma, and thus for the
                    % distinction of `lastsep' and `pairsep', a "range" counts
                    % as one.  Since `range_beg' has already been counted
                    % (here or with the first of type), we refrain from
                    % incrementing `ref_count_int'.
                    \bool_lazy_and:nnTF
                      { ! \tl_if_empty_p:N \l_@@_endrangefunc_tl }
                      { \cs_if_exist_p:c { \l_@@_endrangefunc_tl :VVN } }
                      {
                        \use:c { \l_@@_endrangefunc_tl :VVN }
                          \l_@@_range_beg_label_tl
                          \l_@@_label_a_tl
                          \l_@@_range_end_ref_tl
                        \tl_put_right:Nx \l_@@_typeset_queue_curr_tl
                          {
                            \exp_not:V \l_@@_rangesep_tl
                            \@@_get_ref_endrange:VVN
                              \l_@@_label_a_tl
                              \l_@@_range_end_ref_tl
                              \l_@@_refbounds_mid_re_seq
                          }
                      }
                      {
                        \tl_put_right:Nx \l_@@_typeset_queue_curr_tl
                          {
                            \exp_not:V \l_@@_rangesep_tl
                            \@@_get_ref:VN \l_@@_label_a_tl
                              \l_@@_refbounds_mid_re_seq
                          }
                      }
                  }
              }
            % We just closed a range, reset `range_beg_is_first' in case a
            % second range for the same type occurs, in which case its
            % `range_beg' will no longer be `first'.
            \bool_set_false:N \l_@@_range_beg_is_first_bool
            % Reset counters.
            \int_zero:N \l_@@_range_count_int
            \int_zero:N \l_@@_range_same_count_int
          }
      }
    % Step label counter for next iteration.
    \int_incr:N \l_@@_label_count_int
  }
%    \end{macrocode}
% \end{macro}
%
%
%
% \subsection*{Auxiliary functions}
%
% \cs{@@_get_ref:nN} and \cs{@@_get_ref_first:} are the two functions which
% actually build the reference blocks for typesetting.  \cs{@@_get_ref:nN}
% handles all references but the first of its type, and \cs{@@_get_ref_first:}
% deals with the first reference of a type.  Saying they do ``typesetting'' is
% imprecise though, they actually prepare material to be accumulated in
% \cs{l_@@_typeset_queue_curr_tl} inside \cs{@@_typeset_refs_last_of_type:}
% and \cs{@@_typeset_refs_not_last_of_type:}.  And this difference results
% quite crucial for the \TeX{}nical requirements of these functions.  This
% because, as we are processing the label stack and accumulating content in
% the queue, we are using a number of variables which are transient to the
% current label, the label properties among them, but not only.  Hence, these
% variables \emph{must} be expanded to their current values to be stored in
% the queue.  Indeed, \cs{@@_get_ref:nN} and \cs{@@_get_ref_first:} get
% called, as they must, in the context of \texttt{x} type expansions.  But we
% don't want to expand the values of the variables themselves, so we need to
% get current values, but stop expansion after that.  In particular, reference
% options given by the user should reach the stream for its final typesetting
% (when the queue itself gets typeset) \emph{unmodified} (``no manipulation'',
% to use the \texttt{n} signature jargon).  We also need to prevent premature
% expansion of material that can't be expanded at this point (e.g. grouping,
% \cs{zref@default} or \cs[replace=false]{hyper@@link}).  In a nutshell, the
% job of these two functions is putting the pieces in place, but with proper
% expansion control.
%
%
% \begin{macro}{\@@_ref_default:, \@@_name_default:}
%   Default values for undefined references and undefined type names,
%   respectively.  We are ultimately using \cs{zref@default}, but calls to it
%   should be made through these internal functions, according to the case.
%   As a bonus, we don't need to protect them with \cs{exp_not:N}, as
%   \cs{zref@default} would require, since we already define them protected.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_ref_default:
  { \zref@default }
\cs_new_protected:Npn \@@_name_default:
  { \zref@default }
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}{\@@_get_ref:nN}
%   Handles a complete reference block to be accumulated in the ``queue'',
%   including refbounds, and hyperlinking.  For use with all labels, except
%   the first of its type, which is done by \cs{@@_get_ref_first:}, and the
%   last of a range, which is done by \cs{@@_get_ref_endrange:nnN}.
%   \begin{syntax}
%     \cs{@@_get_ref:nN} \Arg{label} \Arg{refbounds}
%   \end{syntax}
%    \begin{macrocode}
\cs_new:Npn \@@_get_ref:nN #1#2
  {
    \zref@ifrefcontainsprop {#1} { \l_@@_ref_property_tl }
      {
        \bool_if:nTF
          {
            \l_@@_hyperlink_bool &&
            ! \l_@@_link_star_bool
          }
          {
            \seq_item:Nn #2 { 1 }
            \@@_hyperlink:nnn
              { \@@_extract_url_unexp:n {#1} }
              { \@@_extract_unexp:nnn {#1} { anchor } { } }
              {
                \seq_item:Nn #2 { 2 }
                \exp_not:N \group_begin:
                \exp_not:V \l_@@_reffont_tl
                \@@_extract_unexp:nvn {#1}
                  { l_@@_ref_property_tl } { }
                \exp_not:N \group_end:
                \seq_item:Nn #2 { 3 }
              }
            \seq_item:Nn #2 { 4 }
          }
          {
            \seq_item:Nn #2 { 1 }
            \seq_item:Nn #2 { 2 }
            \exp_not:N \group_begin:
            \exp_not:V \l_@@_reffont_tl
            \@@_extract_unexp:nvn {#1}
              { l_@@_ref_property_tl } { }
            \exp_not:N \group_end:
            \seq_item:Nn #2 { 3 }
            \seq_item:Nn #2 { 4 }
          }
      }
      { \@@_ref_default: }
  }
\cs_generate_variant:Nn \@@_get_ref:nN { VN }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_get_ref_endrange:nnN}
%   \begin{syntax}
%     \cs{@@_get_ref_endrange:nnN} \Arg{label} \Arg{reference} \Arg{refbounds}
%   \end{syntax}
%    \begin{macrocode}
\cs_new:Npn \@@_get_ref_endrange:nnN #1#2#3
  {
    \str_if_eq:nnTF {#2} { zc@missingproperty }
      { \@@_ref_default: }
      {
        \bool_if:nTF
          {
            \l_@@_hyperlink_bool &&
            ! \l_@@_link_star_bool
          }
          {
            \seq_item:Nn #3 { 1 }
            \@@_hyperlink:nnn
              { \@@_extract_url_unexp:n {#1} }
              { \@@_extract_unexp:nnn {#1} { anchor } { } }
              {
                \seq_item:Nn #3 { 2 }
                \exp_not:N \group_begin:
                \exp_not:V \l_@@_reffont_tl
                \exp_not:n {#2}
                \exp_not:N \group_end:
                \seq_item:Nn #3 { 3 }
              }
            \seq_item:Nn #3 { 4 }
          }
          {
            \seq_item:Nn #3 { 1 }
            \seq_item:Nn #3 { 2 }
            \exp_not:N \group_begin:
            \exp_not:V \l_@@_reffont_tl
            \exp_not:n {#2}
            \exp_not:N \group_end:
            \seq_item:Nn #3 { 3 }
            \seq_item:Nn #3 { 4 }
          }
      }
  }
\cs_generate_variant:Nn \@@_get_ref_endrange:nnN { VVN }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_get_ref_first:}
%   Handles a complete reference block for the first label of its type to be
%   accumulated in the ``queue'', including ``pre'' and ``pos'' elements,
%   hyperlinking, and the reference type ``name''.  It does not receive
%   arguments, but relies on being called in the appropriate place in
%   \cs{@@_typeset_refs_last_of_type:} where a number of variables are
%   expected to be appropriately set for it to consume.  Prominently among
%   those is \cs{l_@@_type_first_label_tl}, but it also expected to be called
%   right after \cs{@@_type_name_setup:} which sets \cs{l_@@_type_name_tl} and
%   \cs{l_@@_name_in_link_bool} which it uses.
%    \begin{macrocode}
\cs_new:Npn \@@_get_ref_first:
  {
    \zref@ifrefundefined { \l_@@_type_first_label_tl }
      { \@@_ref_default: }
      {
        \bool_if:NTF \l_@@_name_in_link_bool
          {
            \zref@ifrefcontainsprop
              { \l_@@_type_first_label_tl }
              { \l_@@_ref_property_tl }
              {
                \@@_hyperlink:nnn
                  {
                    \@@_extract_url_unexp:V
                      \l_@@_type_first_label_tl
                  }
                  {
                    \@@_extract_unexp:Vnn
                      \l_@@_type_first_label_tl { anchor } { }
                  }
                  {
                    \exp_not:N \group_begin:
                    \exp_not:V \l_@@_namefont_tl
                    \exp_not:V \l_@@_type_name_tl
                    \exp_not:N \group_end:
                    \exp_not:V \l_@@_namesep_tl
                    \seq_item:Nn \l_@@_type_first_refbounds_seq { 1 }
                    \seq_item:Nn \l_@@_type_first_refbounds_seq { 2 }
                    \exp_not:N \group_begin:
                    \exp_not:V \l_@@_reffont_tl
                    \@@_extract_unexp:Vvn
                      \l_@@_type_first_label_tl
                      { l_@@_ref_property_tl } { }
                    \exp_not:N \group_end:
                    \seq_item:Nn \l_@@_type_first_refbounds_seq { 3 }
                  }
                \seq_item:Nn \l_@@_type_first_refbounds_seq { 4 }
              }
              {
                \exp_not:N \group_begin:
                \exp_not:V \l_@@_namefont_tl
                \exp_not:V \l_@@_type_name_tl
                \exp_not:N \group_end:
                \exp_not:V \l_@@_namesep_tl
                \@@_ref_default:
              }
          }
          {
            \bool_if:nTF \l_@@_type_name_missing_bool
              {
                \@@_name_default:
                \exp_not:V \l_@@_namesep_tl
              }
              {
                \exp_not:N \group_begin:
                \exp_not:V \l_@@_namefont_tl
                \exp_not:V \l_@@_type_name_tl
                \exp_not:N \group_end:
                \tl_if_empty:NF \l_@@_type_name_tl
                  { \exp_not:V \l_@@_namesep_tl }
              }
            \zref@ifrefcontainsprop
              { \l_@@_type_first_label_tl }
              { \l_@@_ref_property_tl }
              {
                \bool_if:nTF
                  {
                    \l_@@_hyperlink_bool &&
                    ! \l_@@_link_star_bool
                  }
                  {
                    \seq_item:Nn
                      \l_@@_type_first_refbounds_seq { 1 }
                    \@@_hyperlink:nnn
                      {
                        \@@_extract_url_unexp:V
                          \l_@@_type_first_label_tl
                      }
                      {
                        \@@_extract_unexp:Vnn
                          \l_@@_type_first_label_tl { anchor } { }
                      }
                      {
                        \seq_item:Nn
                          \l_@@_type_first_refbounds_seq { 2 }
                        \exp_not:N \group_begin:
                        \exp_not:V \l_@@_reffont_tl
                        \@@_extract_unexp:Vvn
                          \l_@@_type_first_label_tl
                          { l_@@_ref_property_tl } { }
                        \exp_not:N \group_end:
                        \seq_item:Nn
                          \l_@@_type_first_refbounds_seq { 3 }
                      }
                    \seq_item:Nn
                      \l_@@_type_first_refbounds_seq { 4 }
                  }
                  {
                    \seq_item:Nn \l_@@_type_first_refbounds_seq { 1 }
                    \seq_item:Nn \l_@@_type_first_refbounds_seq { 2 }
                    \exp_not:N \group_begin:
                    \exp_not:V \l_@@_reffont_tl
                    \@@_extract_unexp:Vvn
                      \l_@@_type_first_label_tl
                      { l_@@_ref_property_tl } { }
                    \exp_not:N \group_end:
                    \seq_item:Nn \l_@@_type_first_refbounds_seq { 3 }
                    \seq_item:Nn \l_@@_type_first_refbounds_seq { 4 }
                  }
              }
              { \@@_ref_default: }
          }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_type_name_setup:}
%   Auxiliary function to \cs{@@_typeset_refs_last_of_type:}.  It is
%   responsible for setting the type name variable \cs{l_@@_type_name_tl} and
%   \cs{l_@@_name_in_link_bool}.  If a type name can't be found,
%   \cs{l_@@_type_name_tl} is cleared.  The function takes no arguments, but
%   is expected to be called in \cs{@@_typeset_refs_last_of_type:} right
%   before \cs{@@_get_ref_first:}, which is the main consumer of the variables
%   it sets, though not the only one (and hence this cannot be moved into
%   \cs{@@_get_ref_first:} itself).  It also expects a number of relevant
%   variables to have been appropriately set, and which it uses, prominently
%   \cs{l_@@_type_first_label_type_tl}, but also the queue itself in
%   \cs{l_@@_typeset_queue_curr_tl}, which should be ``ready except for the
%   first label'', and the type counter \cs{l_@@_type_count_int}.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_type_name_setup:
  {
    \zref@ifrefundefined { \l_@@_type_first_label_tl }
      {
        \tl_clear:N \l_@@_type_name_tl
        \bool_set_true:N \l_@@_type_name_missing_bool
      }
      {
        \tl_if_eq:NnTF
          \l_@@_type_first_label_type_tl { zc@missingtype }
          {
            \tl_clear:N \l_@@_type_name_tl
            \bool_set_true:N \l_@@_type_name_missing_bool
          }
          {
            % Determine whether we should use capitalization, abbreviation,
            % and plural.
            \bool_lazy_or:nnTF
              { \l_@@_cap_bool }
              {
                \l_@@_capfirst_bool &&
                \int_compare_p:nNn { \l_@@_type_count_int } = { 0 }
              }
              { \tl_set:Nn \l_@@_name_format_tl {Name} }
              { \tl_set:Nn \l_@@_name_format_tl {name} }
            % If the queue is empty, we have a singular, otherwise, plural.
            \tl_if_empty:NTF \l_@@_typeset_queue_curr_tl
              { \tl_put_right:Nn \l_@@_name_format_tl { -sg } }
              { \tl_put_right:Nn \l_@@_name_format_tl { -pl } }
            \bool_lazy_and:nnTF
              { \l_@@_abbrev_bool }
              {
                ! \int_compare_p:nNn
                    { \l_@@_type_count_int } = { 0 } ||
                ! \l_@@_noabbrev_first_bool
              }
              {
                \tl_set:NV \l_@@_name_format_fallback_tl
                  \l_@@_name_format_tl
                \tl_put_right:Nn \l_@@_name_format_tl { -ab }
              }
              { \tl_clear:N \l_@@_name_format_fallback_tl }

            % Handle number and gender nudges.
            \bool_if:NT \l_@@_nudge_enabled_bool
              {
                \bool_if:NTF \l_@@_nudge_singular_bool
                  {
                    \tl_if_empty:NF \l_@@_typeset_queue_curr_tl
                      {
                        \msg_warning:nnx { zref-clever }
                          { nudge-plural-when-sg }
                          { \l_@@_type_first_label_type_tl }
                      }
                  }
                  {
                    \bool_lazy_all:nT
                      {
                        { \l_@@_nudge_comptosing_bool }
                        { \tl_if_empty_p:N \l_@@_typeset_queue_curr_tl }
                        {
                          \int_compare_p:nNn
                            { \l_@@_label_count_int } > { 0 }
                        }
                      }
                      {
                        \msg_warning:nnx { zref-clever }
                          { nudge-comptosing }
                          { \l_@@_type_first_label_type_tl }
                      }
                  }
                \bool_lazy_and:nnT
                  { \l_@@_nudge_gender_bool }
                  { ! \tl_if_empty_p:N \l_@@_ref_gender_tl }
                  {
                    \@@_get_rf_opt_seq:nxxN { gender }
                      { \l_@@_type_first_label_type_tl }
                      { \l_@@_ref_language_tl }
                      \l_@@_type_name_gender_seq
                    \seq_if_in:NVF
                      \l_@@_type_name_gender_seq
                      \l_@@_ref_gender_tl
                      {
                        \seq_if_empty:NTF \l_@@_type_name_gender_seq
                          {
                            \msg_warning:nnxxx { zref-clever }
                              { nudge-gender-not-declared-for-type }
                              { \l_@@_ref_gender_tl }
                              { \l_@@_type_first_label_type_tl }
                              { \l_@@_ref_language_tl }
                          }
                          {
                            \msg_warning:nnxxxx { zref-clever }
                              { nudge-gender-mismatch }
                              { \l_@@_type_first_label_type_tl }
                              { \l_@@_ref_gender_tl }
                              {
                                \seq_use:Nn
                                  \l_@@_type_name_gender_seq { ,~ }
                              }
                              { \l_@@_ref_language_tl }
                          }
                      }
                  }
              }

            \tl_if_empty:NTF \l_@@_name_format_fallback_tl
              {
                \@@_opt_tl_get:cNF
                  {
                    \@@_opt_varname_type:een
                      { \l_@@_type_first_label_type_tl }
                      { \l_@@_name_format_tl }
                      { tl }
                  }
                  \l_@@_type_name_tl
                  {
                    \tl_if_empty:NF \l_@@_ref_decl_case_tl
                      {
                        \tl_put_left:Nn \l_@@_name_format_tl { - }
                        \tl_put_left:NV \l_@@_name_format_tl
                          \l_@@_ref_decl_case_tl
                      }
                    \@@_opt_tl_get:cNF
                      {
                        \@@_opt_varname_lang_type:eeen
                          { \l_@@_ref_language_tl }
                          { \l_@@_type_first_label_type_tl }
                          { \l_@@_name_format_tl }
                          { tl }
                      }
                      \l_@@_type_name_tl
                      {
                        \tl_clear:N \l_@@_type_name_tl
                        \bool_set_true:N \l_@@_type_name_missing_bool
                        \msg_warning:nnxx { zref-clever } { missing-name }
                          { \l_@@_name_format_tl }
                          { \l_@@_type_first_label_type_tl }
                      }
                  }
              }
              {
                \@@_opt_tl_get:cNF
                  {
                    \@@_opt_varname_type:een
                      { \l_@@_type_first_label_type_tl }
                      { \l_@@_name_format_tl }
                      { tl }
                  }
                  \l_@@_type_name_tl
                  {
                    \@@_opt_tl_get:cNF
                      {
                        \@@_opt_varname_type:een
                          { \l_@@_type_first_label_type_tl }
                          { \l_@@_name_format_fallback_tl }
                          { tl }
                      }
                      \l_@@_type_name_tl
                      {
                        \tl_if_empty:NF \l_@@_ref_decl_case_tl
                          {
                            \tl_put_left:Nn
                              \l_@@_name_format_tl { - }
                            \tl_put_left:NV \l_@@_name_format_tl
                              \l_@@_ref_decl_case_tl
                            \tl_put_left:Nn
                              \l_@@_name_format_fallback_tl { - }
                            \tl_put_left:NV
                              \l_@@_name_format_fallback_tl
                              \l_@@_ref_decl_case_tl
                          }
                        \@@_opt_tl_get:cNF
                          {
                            \@@_opt_varname_lang_type:eeen
                              { \l_@@_ref_language_tl }
                              { \l_@@_type_first_label_type_tl }
                              { \l_@@_name_format_tl }
                              { tl }
                          }
                          \l_@@_type_name_tl
                          {
                            \@@_opt_tl_get:cNF
                              {
                                \@@_opt_varname_lang_type:eeen
                                  { \l_@@_ref_language_tl }
                                  { \l_@@_type_first_label_type_tl }
                                  { \l_@@_name_format_fallback_tl }
                                  { tl }
                              }
                              \l_@@_type_name_tl
                              {
                                \tl_clear:N \l_@@_type_name_tl
                                \bool_set_true:N
                                  \l_@@_type_name_missing_bool
                                \msg_warning:nnxx { zref-clever }
                                  { missing-name }
                                  { \l_@@_name_format_tl }
                                  { \l_@@_type_first_label_type_tl }
                              }
                          }
                      }
                  }
              }
          }
      }

    % Signal whether the type name is to be included in the hyperlink or not.
    \bool_lazy_any:nTF
      {
        { ! \l_@@_hyperlink_bool }
        { \l_@@_link_star_bool }
        { \tl_if_empty_p:N \l_@@_type_name_tl }
        { \str_if_eq_p:Vn \l_@@_nameinlink_str { false } }
      }
      { \bool_set_false:N \l_@@_name_in_link_bool }
      {
        \bool_lazy_any:nTF
          {
            { \str_if_eq_p:Vn \l_@@_nameinlink_str { true } }
            {
              \str_if_eq_p:Vn \l_@@_nameinlink_str { tsingle } &&
              \tl_if_empty_p:N \l_@@_typeset_queue_curr_tl
            }
            {
              \str_if_eq_p:Vn \l_@@_nameinlink_str { single } &&
              \tl_if_empty_p:N \l_@@_typeset_queue_curr_tl &&
              \l_@@_typeset_last_bool &&
              \int_compare_p:nNn { \l_@@_type_count_int } = { 0 }
            }
          }
          { \bool_set_true:N \l_@@_name_in_link_bool }
          { \bool_set_false:N \l_@@_name_in_link_bool }
      }
  }
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}{\@@_hyperlink:nnn}
%   This avoids using the internal \cs[replace=false]{hyper@@link}, using only
%   public \pkg{hyperref} commands (see
%   \url{https://github.com/latex3/hyperref/issues/229#issuecomment-1093870142},
%   thanks \contributor{Ulrike Fisher}).
%   \begin{syntax}
%     \cs{@@_hyperlink:nnn} \Arg{url/file} \Arg{anchor} \Arg{text}
%   \end{syntax}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_hyperlink:nnn #1#2#3
  {
    \tl_if_empty:nTF {#1}
      { \hyperlink {#2} {#3} }
      { \hyper@linkfile {#3} {#1} {#2} }
  }
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}{\@@_extract_url_unexp:n}
%   A convenience auxiliary function for extraction of the \texttt{url} /
%   \texttt{urluse} property, provided by the \pkg{zref-xr} module.  Ensure
%   that, in the context of an x expansion, \cs{zref@extractdefault} is
%   expanded exactly twice, but no further to retrieve the proper value.  See
%   documentation for \cs{@@_extract_unexp:nnn}.
%    \begin{macrocode}
\cs_new:Npn \@@_extract_url_unexp:n #1
  {
    \zref@ifpropundefined { urluse }
      { \@@_extract_unexp:nnn {#1} { url } { } }
      {
        \zref@ifrefcontainsprop {#1} { urluse }
          { \@@_extract_unexp:nnn {#1} { urluse } { } }
          { \@@_extract_unexp:nnn {#1} { url } { } }
      }
  }
\cs_generate_variant:Nn \@@_extract_url_unexp:n { V }
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}{\@@_labels_in_sequence:nn}
%   Auxiliary function to \cs{@@_typeset_refs_not_last_of_type:}. Sets
%   \cs{l_@@_next_maybe_range_bool} to true if \meta{label b} comes in
%   immediate sequence from \meta{label a}.  And sets both
%   \cs{l_@@_next_maybe_range_bool} and \cs{l_@@_next_is_same_bool} to true if
%   the two labels are the ``same'' (that is, have the same counter value).
%   These two boolean variables are the basis for all range and compression
%   handling inside \cs{@@_typeset_refs_not_last_of_type:}, so this function
%   is expected to be called at its beginning, if compression is enabled.
%   \begin{syntax}
%     \cs{@@_labels_in_sequence:nn} \Arg{label a} \Arg{label b}
%   \end{syntax}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_labels_in_sequence:nn #1#2
  {
    \exp_args:Nxx \tl_if_eq:nnT
      { \@@_extract_unexp:nnn {#1} { externaldocument } { } }
      { \@@_extract_unexp:nnn {#2} { externaldocument } { } }
      {
        \tl_if_eq:NnTF \l_@@_ref_property_tl { page }
          {
            \exp_args:Nxx \tl_if_eq:nnT
              { \@@_extract_unexp:nnn {#1} { zc@pgfmt } { } }
              { \@@_extract_unexp:nnn {#2} { zc@pgfmt } { } }
              {
                \int_compare:nNnTF
                  { \@@_extract:nnn {#1} { zc@pgval } { -2 } + 1 }
                    =
                  { \@@_extract:nnn {#2} { zc@pgval } { -1 } }
                  { \bool_set_true:N \l_@@_next_maybe_range_bool }
                  {
                    \int_compare:nNnT
                      { \@@_extract:nnn {#1} { zc@pgval } { -1 } }
                        =
                      { \@@_extract:nnn {#2} { zc@pgval } { -1 } }
                      {
                        \bool_set_true:N \l_@@_next_maybe_range_bool
                        \bool_set_true:N \l_@@_next_is_same_bool
                      }
                  }
              }
          }
          {
            \exp_args:Nxx \tl_if_eq:nnT
              { \@@_extract_unexp:nnn {#1} { zc@counter } { } }
              { \@@_extract_unexp:nnn {#2} { zc@counter } { } }
              {
                \exp_args:Nxx \tl_if_eq:nnT
                  { \@@_extract_unexp:nnn {#1} { zc@enclval } { } }
                  { \@@_extract_unexp:nnn {#2} { zc@enclval } { } }
                  {
                    \int_compare:nNnTF
                      { \@@_extract:nnn {#1} { zc@cntval } { -2 } + 1 }
                        =
                      { \@@_extract:nnn {#2} { zc@cntval } { -1 } }
                      { \bool_set_true:N \l_@@_next_maybe_range_bool }
                      {
                        \int_compare:nNnT
                          { \@@_extract:nnn {#1} { zc@cntval } { -1 } }
                            =
                          { \@@_extract:nnn {#2} { zc@cntval } { -1 } }
                          {
%    \end{macrocode}
% If \texttt{zc@counter}s are equal, \texttt{zc@enclval}s are equal, and
% \texttt{zc@enclval}s are equal, but the references themselves are different,
% this means that \cs{@currentlabel} has somehow been set manually (e.g. by an
% \pkg{amsmath}'s \cs{tag}), in which case we have no idea what's in there,
% and we should not even consider this is still a range.  If they are equal,
% though, of course it is a range, and it is the same.
%    \begin{macrocode}
                            \exp_args:Nxx \tl_if_eq:nnT
                              {
                                \@@_extract_unexp:nvn {#1}
                                  { l_@@_ref_property_tl } { }
                              }
                              {
                                \@@_extract_unexp:nvn {#2}
                                  { l_@@_ref_property_tl } { }
                              }
                              {
                                \bool_set_true:N
                                  \l_@@_next_maybe_range_bool
                                \bool_set_true:N
                                  \l_@@_next_is_same_bool
                              }
                          }
                      }
                  }
              }
          }
      }
  }
%    \end{macrocode}
% \end{macro}
%
%
%
% Finally, some functions for retrieving reference options values, according
% to the relevant precedence rules.  They receive an \meta{option} as
% argument, and store the retrieved value in an appropriate \meta{variable}.
% The difference between each of these functions is the data type of the
% option each should be used for.
%
%
% \begin{macro}{\@@_get_rf_opt_tl:nnnN}
%   \begin{syntax}
%     \cs{@@_get_rf_opt_tl:nnnN} \Arg{option}
%     ~~\Arg{ref type} \Arg{language} \Arg{tl variable}
%   \end{syntax}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_get_rf_opt_tl:nnnN #1#2#3#4
  {
    % First attempt: general options.
    \@@_opt_tl_get:cNF
      { \@@_opt_varname_general:nn {#1} { tl } }
      #4
      {
        % If not found, try type specific options.
        \@@_opt_tl_get:cNF
          { \@@_opt_varname_type:nnn {#2} {#1} { tl } }
          #4
          {
            % If not found, try type- and language-specific.
            \@@_opt_tl_get:cNF
              { \@@_opt_varname_lang_type:nnnn {#3} {#2} {#1} { tl } }
              #4
              {
                % If not found, try language-specific default.
                \@@_opt_tl_get:cNF
                  { \@@_opt_varname_lang_default:nnn {#3} {#1} { tl } }
                  #4
                  {
                    % If not found, try fallback.
                    \@@_opt_tl_get:cNF
                      { \@@_opt_varname_fallback:nn {#1} { tl } }
                      #4
                      { \tl_clear:N #4 }
                  }
              }
          }
      }
  }
\cs_generate_variant:Nn \@@_get_rf_opt_tl:nnnN { nxxN }
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}{\@@_get_rf_opt_seq:nnnN}
%   \begin{syntax}
%     \cs{@@_get_rf_opt_seq:nnnN} \Arg{option}
%     ~~\Arg{ref type} \Arg{language} \Arg{seq variable}
%   \end{syntax}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_get_rf_opt_seq:nnnN #1#2#3#4
  {
    % First attempt: general options.
    \@@_opt_seq_get:cNF
      { \@@_opt_varname_general:nn {#1} { seq } }
      #4
      {
        % If not found, try type specific options.
        \@@_opt_seq_get:cNF
          { \@@_opt_varname_type:nnn {#2} {#1} { seq } }
          #4
          {
            % If not found, try type- and language-specific.
            \@@_opt_seq_get:cNF
              { \@@_opt_varname_lang_type:nnnn {#3} {#2} {#1} { seq } }
              #4
              {
                % If not found, try language-specific default.
                \@@_opt_seq_get:cNF
                  { \@@_opt_varname_lang_default:nnn {#3} {#1} { seq } }
                  #4
                  {
                    % If not found, try fallback.
                    \@@_opt_seq_get:cNF
                      { \@@_opt_varname_fallback:nn {#1} { seq } }
                      #4
                      { \seq_clear:N #4 }
                  }
              }
          }
      }
  }
\cs_generate_variant:Nn \@@_get_rf_opt_seq:nnnN { nxxN }
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}{\@@_get_rf_opt_bool:nnnnN}
%   \begin{syntax}
%     \cs{@@_get_rf_opt_bool:nN} \Arg{option} \Arg{default}
%     ~~\Arg{ref type} \Arg{language}  \Arg{bool variable}
%   \end{syntax}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_get_rf_opt_bool:nnnnN #1#2#3#4#5
  {
    % First attempt: general options.
    \@@_opt_bool_get:cNF
      { \@@_opt_varname_general:nn {#1} { bool } }
      #5
      {
        % If not found, try type specific options.
        \@@_opt_bool_get:cNF
          { \@@_opt_varname_type:nnn {#3} {#1} { bool } }
          #5
          {
            % If not found, try type- and language-specific.
            \@@_opt_bool_get:cNF
              { \@@_opt_varname_lang_type:nnnn {#4} {#3} {#1} { bool } }
              #5
              {
                % If not found, try language-specific default.
                \@@_opt_bool_get:cNF
                  { \@@_opt_varname_lang_default:nnn {#4} {#1} { bool } }
                  #5
                  {
                    % If not found, try fallback.
                    \@@_opt_bool_get:cNF
                      { \@@_opt_varname_fallback:nn {#1} { bool } }
                      #5
                      { \use:c { bool_set_ #2 :N } #5 }
                  }
              }
          }
      }
  }
\cs_generate_variant:Nn \@@_get_rf_opt_bool:nnnnN { nnxxN }
%    \end{macrocode}
% \end{macro}
%
%
%
% \section{Compatibility}
%
% This section is meant to aggregate any ``special handling'' needed for
% \LaTeX{} kernel features, document classes, and packages, needed for
% \pkg{zref-clever} to work properly with them.
%
%
% \subsection{\opt{appendix}}
%
% One relevant case of different reference types sharing the same counter is
% the \cs{appendix} which in some document classes, including the standard
% ones, change the sectioning commands looks but, of course, keep using the
% same counter.  \file{book.cls} and \file{report.cls} reset counters
% \texttt{chapter} and \texttt{section} to 0, change \cs{@chapapp} to use
% \cs{appendixname} and use \cs{@Alph} for \cs{thechapter}. \file{article.cls}
% resets counters \texttt{section} and \texttt{subsection} to 0, and uses
% \cs{@Alph} for \cs{thesection}.  \file{memoir.cls}, \file{scrbook.cls} and
% \file{scrarticle.cls} do the same as their corresponding standard classes,
% and sometimes a little more, but what interests us here is pretty much the
% same.  See also the \pkg{appendix} package.
%
% The standard \cs{appendix} command is a one way switch, in other words, it
% cannot be reverted (see \url{https://tex.stackexchange.com/a/444057}).  So,
% even if the fact that it is a ``switch'' rather than an environment
% complicates things, because we have to make ungrouped settings to correspond
% to its effects, in practice this is not a big deal, since these settings are
% never really reverted (by default, at least).  Hence, hooking into
% \cs{appendix} is a viable and natural alternative.  The \cls{memoir} class
% and the \pkg{appendix} package define the \texttt{appendices} and
% \texttt{subappendices} environments, which provide for a way for the
% appendix to ``end'', but in this case, of course, we can hook into the
% environment instead.
%
%    \begin{macrocode}
\@@_compat_module:nn { appendix }
  {
    \AddToHook { cmd / appendix / before }
      {
        \@@_zcsetup:n
          {
            countertype =
              {
                chapter       = appendix ,
                section       = appendix ,
                subsection    = appendix ,
                subsubsection = appendix ,
                paragraph     = appendix ,
                subparagraph  = appendix ,
              }
          }
      }
  }
%    \end{macrocode}
%
% Depending on the definition of \cs{appendix}, using the hook may lead to
% trouble with the first released version of \pkg{ltcmdhooks} (the one
% released with the 2021-06-01 kernel).  Particularly, if the definition of
% the command being hooked at contains a double hash mark (\texttt{\#\#}) the
% patch to add the hook, if it needs to be done with the \cs{scantokens}
% method, may fail noisily (see \url{https://tex.stackexchange.com/q/617905},
% with a detailed explanation and possible workaround by \contributor{Phelype
% Oleinik}).  The 2021-11-15 kernel release already handles this gracefully,
% thanks to fix by \contributor{Phelype Oleinik} at
% \url{https://github.com/latex3/latex2e/pull/699}.
%
%
% \subsection{\opt{appendices}}
%
% This module applies both to the \pkg{appendix} package, and to the
% \cls{memoir} class, since it ``emulates'' the package.
%
%    \begin{macrocode}
\@@_compat_module:nn { appendices }
  {
    \@@_if_package_loaded:nT { appendix }
      {
        \newcounter { zc@appendix }
        \newcounter { zc@save@appendix }
        \setcounter { zc@appendix } { 0 }
        \setcounter { zc@save@appendix } { 0 }
        \cs_if_exist:cTF { chapter }
          {
            \@@_zcsetup:n
              { counterresetby = { chapter = zc@appendix } }
          }
          {
            \cs_if_exist:cT { section }
              {
                \@@_zcsetup:n
                  { counterresetby = { section = zc@appendix } }
              }
          }
        \AddToHook { env / appendices / begin }
          {
            \stepcounter { zc@save@appendix }
            \setcounter { zc@appendix } { \value { zc@save@appendix } }
            \@@_zcsetup:n
              {
                countertype =
                  {
                    chapter       = appendix ,
                    section       = appendix ,
                    subsection    = appendix ,
                    subsubsection = appendix ,
                    paragraph     = appendix ,
                    subparagraph  = appendix ,
                  }
              }
          }
        \AddToHook { env / appendices / end }
          { \setcounter { zc@appendix } { 0 } }
        \AddToHook { cmd / appendix / before }
          {
            \stepcounter { zc@save@appendix }
            \setcounter { zc@appendix } { \value { zc@save@appendix } }
          }
        \AddToHook { env / subappendices / begin }
          {
            \@@_zcsetup:n
              {
                countertype =
                  {
                    section       = appendix ,
                    subsection    = appendix ,
                    subsubsection = appendix ,
                    paragraph     = appendix ,
                    subparagraph  = appendix ,
                  } ,
              }
          }
        \msg_info:nnn { zref-clever } { compat-package } { appendix }
      }
  }
%    \end{macrocode}
%
%
%
% \subsection{\opt{memoir}}
%
% The \cls{memoir} document class has quite a number of cross-referencing
% related features, mostly dealing with captions, subfloats, and notes.  Some
% of them are implemented in ways which make difficult the use of \pkg{zref},
% particularly \cs{zlabel}, short of redefining the whole stuff ourselves.
% Hopefully, these features are specialized enough to make \pkg{zref-clever}
% useful enough with \cls{memoir} without much friction, but unless some
% support is added upstream, it is difficult not to be a little intrusive
% here.
%
% \begin{enumerate}
% \item Caption functionality which receives \meta{label} as optional
%   argument, namely:
%   \begin{enumerate}
%   \item The \env{sidecaption} and \env{sidecontcaption} environments.  These
%     environments \emph{store} the label in an internal macro,
%     \cs{m@mscaplabel}, at the begin environment code (more precisely in
%     \cs[replace=false]{@@sidecaption}), but both the call to
%     \cs{refstepcounter} and the expansion of \cs{m@mscaplabel} take place at
%     \cs{endsidecaption}.  For this reason, hooks are not particularly
%     helpful, and there is not any easy way to grab the \meta{label} argument
%     to start with.  I can see two ways to deal with these environments, none
%     of which I really like.  First, map through \cs{m@mscaplabel} until
%     \cs{label} is found, then grab the next token which is the \meta{label}.
%     This can be used to set a \cs{zlabel} either with a kernel environment
%     hook, or with \cs{@mem@scap@afterhook} (the former requires running
%     \cs{refstepcounter} on our own, since the \texttt{env/.../end} hook
%     comes before this is done by \cs{endsidecaption}).  Second, locally
%     redefine \cs{label} to set both labels inside the environments.
%   \item The bilingual caption commands: \cs{bitwonumcaption},
%     \cs{bionenumcaption}, and \cs{bicaption}.  These commands do not support
%     setting the label in their arguments (the labels do get set, but they
%     end up included in the \texttt{title} property of the label too).  So we
%     do the same for them as for \env{sidecaption}, just taking care of
%     grouping, since we can't count on the convenience of the environment
%     hook (luckily for us, they are scoped themselves, so we can add an extra
%     group there).
%   \end{enumerate}
% \item The \cs{subcaptionref} command, which makes a reference to the
%   subcaption without the number of the main caption (e.g. ``(b)'', instead
%   of ``2.3(b)''), for labels set inside the \meta{subtitle} argument of the
%   subcaptioning commands, namely: \cs{subcaption}, \cs{contsubcaption},
%   \cs{subbottom}, \cs{contsubbottom}, \cs{subtop}.  This functionality is
%   implemented by \cls{memoir} by setting a \emph{second label} with prefix
%   \texttt{sub@\meta{label}}, and storing there just that part of interest.
%   With \pkg{zref} this part is easier, since we can just add an extra
%   property and retrieve it later on.  The thing is that it is hard to find a
%   place to hook into to add the property to the \texttt{main} list, since
%   \cls{memoir} does not really consider the possibility of some other
%   command setting labels.  \cs{@memsubcaption} is the best place to hook I
%   could find.  It is used by subcaptioning commands, and only those.  And
%   there is no hope for an environment hook in this case anyway.
% \item \cls{memoir}'s \cs{footnote}, \cs{verbfootnote}, \cs{sidefootnote} and
%   \cs{pagenote}, just as the regular \cs{footnote} until recently in the
%   kernel, do not set \cs{@currentcounter} alongside \cs{@currentlabel},
%   proper referencing to them requires setting the type for it.
% \item Note that \cls{memoir}'s appendix features ``emulates'' the
%   \pkg{appendix} package, hence the corresponding compatibility module is
%   loaded for \cls{memoir} even if that package is not itself loaded.  The
%   same is true for the \cs{appendix} command module, since it is also
%   defined.
% \end{enumerate}
%
%
%    \begin{macrocode}
\@@_compat_module:nn { memoir }
  {
    \@@_if_class_loaded:nT { memoir }
      {
%    \end{macrocode}
% Add subfigure and subtable support out of the box.  Technically, this is not
% ``default'' behavior for \cls{memoir}, users have to enable it with
% \cs{newsubfloat}, but let this be smooth.  Still, this does not cover any
% other floats created with \cs{newfloat}.  Also include setup for
% \env{verse}.
%    \begin{macrocode}
        \@@_zcsetup:n
          {
            countertype =
              {
                subfigure = figure ,
                subtable  = table ,
                poemline  = line ,
              } ,
            counterresetby =
              {
                subfigure = figure ,
                subtable  = table ,
              } ,
          }
%    \end{macrocode}
% Support for caption \cls{memoir} features that require that \meta{label} be
% supplied as an optional argument.
%    \begin{macrocode}
        \cs_new_protected:Npn \@@_memoir_both_labels:
          {
            \cs_set_eq:NN \@@_memoir_orig_label:n \label
            \cs_set:Npn \@@_memoir_label_and_zlabel:n ##1
              {
                \@@_memoir_orig_label:n {##1}
                \zlabel{##1}
              }
            \cs_set_eq:NN \label \@@_memoir_label_and_zlabel:n
          }
        \AddToHook { env / sidecaption / begin }
          { \@@_memoir_both_labels: }
        \AddToHook { env / sidecontcaption / begin }
          { \@@_memoir_both_labels: }
        \AddToHook{ cmd / bitwonumcaption / before }
          { \group_begin: \@@_memoir_both_labels: }
        \AddToHook{ cmd / bitwonumcaption / after }
          { \group_end: }
        \AddToHook{ cmd / bionenumcaption / before }
          { \group_begin: \@@_memoir_both_labels: }
        \AddToHook{ cmd / bionenumcaption / after }
          { \group_end: }
        \AddToHook{ cmd / bicaption / before }
          { \group_begin: \@@_memoir_both_labels: }
        \AddToHook{ cmd / bicaption / after }
          { \group_end: }
%    \end{macrocode}
% Support for \texttt{subcaption} reference.
%    \begin{macrocode}
        \zref@newprop { subcaption }
          { \cs_if_exist_use:c { @@@@thesub \@captype } }
        \AddToHook{ cmd / @memsubcaption / before }
          { \zref@localaddprop \ZREF@mainlist { subcaption } }
%    \end{macrocode}
% Support for \cs{footnote}, \cs{verbfootnote}, \cs{sidefootnote}, and
% \cs{pagenote}.
%    \begin{macrocode}
        \tl_new:N \l_@@_memoir_footnote_type_tl
        \tl_set:Nn \l_@@_memoir_footnote_type_tl { footnote }
        \AddToHook { env / minipage / begin }
          { \tl_set:Nn \l_@@_memoir_footnote_type_tl { mpfootnote } }
        \AddToHook { cmd / @makefntext / before }
          {
            \@@_zcsetup:x
              { currentcounter = \l_@@_memoir_footnote_type_tl }
          }
        \AddToHook { cmd / @makesidefntext / before }
          { \@@_zcsetup:n { currentcounter = sidefootnote } }
        \@@_zcsetup:n
          {
            countertype =
              {
                sidefootnote = footnote ,
                pagenote = endnote ,
              } ,
          }
        \AddToHook { file / \jobname.ent / before }
          { \@@_zcsetup:x { currentcounter = pagenote } }
        \msg_info:nnn { zref-clever } { compat-class } { memoir }
      }
  }
%    \end{macrocode}
%
%
%
% \subsection{\opt{amsmath}}
%
% About this, see \url{https://tex.stackexchange.com/a/402297}.
%
%    \begin{macrocode}
\@@_compat_module:nn { amsmath }
  {
    \@@_if_package_loaded:nT { amsmath }
      {
%    \end{macrocode}
% First, we define a function for label setting inside \pkg{amsmath} math
% environments, we want it to set both \cs{zlabel} and \cs{label}.  We may
% ``get a ride'', but not steal the place altogether.  This makes for
% potentially redundant labels, but seems a good compromise.  We \emph{must}
% use the lower level \cs{zref@label} in this context, and hence also handle
% protection with \cs{zref@wrapper@babel}, because \cs{zlabel} makes itself
% no-op when \cs{label} is equal to \cs{ltx@gobble}, and that's precisely the
% case inside the \env{multline} environment (and, damn!, I took a beating of
% this detail\dots{}).  See also \url{https://github.com/ho-tex/zref/issues/4}
% and \url{https://github.com/latex3/latex2e/issues/272}.
%    \begin{macrocode}
        \cs_set_nopar:Npn \@@_ltxlabel:n #1
          {
            \@@_orig_ltxlabel:n {#1}
            \zref@wrapper@babel \zref@label {#1}
          }
%    \end{macrocode}
% Then we must store the original value of \cs{ltx@label}, which is the macro
% actually responsible for setting the labels inside \pkg{amsmath}'s math
% environments.  And, after that, redefine it to be \cs{@@_ltxlabel:n}
% instead.  We must handle \pkg{hyperref} here, which comes very late in the
% preamble, and which loads \pkg{nameref} at \texttt{begindocument} (though
% this has changed recently 2022-05-16, see
% \url{https://github.com/latex3/hyperref/commit/a011ba9308a1b047dc151796de557da0bb22abaa}),
% which in turn, lets \cs{ltx@label} be \cs{label}.  This has to come after
% \pkg{nameref}.  Other classes packages also redefine \cs{ltx@label}, which
% may cause some trouble.  A \texttt{grep} on \texttt{texmf-dist} returns hits
% for: \file{thm-restate.sty}, \file{smartref.sty}, \file{jmlrbook.cls},
% \file{cleveref.sty}, \file{cryptocode.sty}, \file{nameref.sty},
% \file{easyeqn.sty}, \file{empheq.sty}, \file{ntheorem.sty},
% \file{nccmath.sty}, \file{nwejm.cls}, \file{nwejmart.cls},
% \file{aguplus.sty}, \file{aguplus.cls}, \file{agupp.sty},
% \file{amsmath.hyp}, \file{amsmath.sty} (surprise!), \file{amsmath.4ht},
% \file{nameref.4ht}, \file{frenchle.sty}, \file{french.sty}, plus
% corresponding documentations and different versions of the same packages.
% That's not too many, but not ``just a few'' either.  The critical ones are
% explicitly handled here (\pkg{amsmath} itself, and \pkg{nameref}).  A number
% of those I'm really not acquainted with.  For \pkg{cleveref}, in particular,
% this procedure is not compatible with it.  If we happen to come later than
% it and override its definition, this may be a substantial problem for
% \pkg{cleveref}, since it will find the label, but it won't contain the data
% it is expecting.  However, this should normally not occur, if the user has
% followed the documented recommendation for \pkg{cleveref} to load it last,
% or at least very late, and besides I don't see much of an use case for using
% both \pkg{cleveref} and \pkg{zref-clever} together.  I have documented in
% the user manual that this module may cause potential issues, and how to work
% around them.  And I have made an upstream feature request for a hook, so
% that this could be made more cleanly at
% \url{https://github.com/latex3/hyperref/issues/212}.
%    \begin{macrocode}
        \@@_if_package_loaded:nTF { hyperref }
          {
            \AddToHook { package / nameref / after }
              {
                \cs_new_eq:NN \@@_orig_ltxlabel:n \ltx@label
                \cs_set_eq:NN \ltx@label \@@_ltxlabel:n
              }
          }
          {
            \cs_new_eq:NN \@@_orig_ltxlabel:n \ltx@label
            \cs_set_eq:NN \ltx@label \@@_ltxlabel:n
          }
%    \end{macrocode}
% The \env{subequations} environment uses \texttt{parentequation} and
% \texttt{equation} as counters, but only the later is subject to
% \cs{refstepcounter}.  What happens is: at the start, \texttt{equation} is
% refstepped, it is then stored in \texttt{parentequation} and set to `0' and,
% at the end of the environment it is restored to the value of
% \texttt{parentequation}.  We cannot even set \cs{@currentcounter} at
% \texttt{env/.../begin}, since the call to
% \cs{refstepcounter}\texttt{\{equation\}} done by \env{subequations} will
% override that in sequence.  Unfortunately, the suggestion to set
% \cs{@currentcounter} to \texttt{parentequation} here was not accepted, see
% \url{https://github.com/latex3/latex2e/issues/687#issuecomment-951451024}
% and subsequent discussion.  So, for \env{subequations}, we really must
% specify manually \opt{currentcounter} and the resetting.  Note that, for
% \env{subequations}, \cs{zlabel} works just fine (that is, if given
% immediately after \texttt{\textbackslash{}begin\{subequations\}}, to refer
% to the parent equation).
%    \begin{macrocode}
        \bool_new:N \l_@@_amsmath_subequations_bool
        \AddToHook { env / subequations / begin }
          {
            \@@_zcsetup:x
              {
                counterresetby =
                  {
                    parentequation =
                      \@@_counter_reset_by:n { equation } ,
                    equation = parentequation ,
                  } ,
                currentcounter = parentequation ,
                countertype = { parentequation = equation } ,
              }
            \bool_set_true:N \l_@@_amsmath_subequations_bool
          }
%    \end{macrocode}
% \pkg{amsmath} does use \cs{refstepcounter} for the \texttt{equation} counter
% throughout and does set \cs{@currentcounter} for \cs{tag}s.  But we still
% have to manually reset \opt{currentcounter} to default because, since we had
% to manually set \opt{currentcounter} to \texttt{parentequation} in
% \env{subequations}, we also have to manually set it to \env{equation} in
% environments which may be used within it.  The \env{xxalignat} environment
% is not included, because it is ``starred'' by default (i.e.\ unnumbered),
% and does not display or accepts labels or tags anyway.  The \env{-ed}
% (\env{gathered}, \env{aligned}, and \env{alignedat}) and \env{cases}
% environments ``must appear within an enclosing math environment''.  Same
% logic applies to other environments defined or redefined by the package,
% like \env{array}, \env{matrix} and variations.  Finally, \env{split} too can
% only be used as part of another environment.  We also arrange, at this
% point, for the provision of the \texttt{subeq} property, for the convenience
% of referring to them directly or to build terse ranges with the
% \opt{endrange} option.
%    \begin{macrocode}
        \zref@newprop { subeq } { \alph { equation } }
        \clist_map_inline:nn
          {
            equation ,
            equation* ,
            align ,
            align* ,
            alignat ,
            alignat* ,
            flalign ,
            flalign* ,
            xalignat ,
            xalignat* ,
            gather ,
            gather* ,
            multline ,
            multline* ,
          }
          {
            \AddToHook { env / #1 / begin }
              {
                \@@_zcsetup:n { currentcounter = equation }
                \bool_if:NT \l_@@_amsmath_subequations_bool
                  { \zref@localaddprop \ZREF@mainlist { subeq } }
              }
          }
        \msg_info:nnn { zref-clever } { compat-package } { amsmath }
      }
  }
%    \end{macrocode}
%
%
%
% \subsection{\opt{mathtools}}
%
% All math environments defined by \pkg{mathtools}, extending the
% \pkg{amsmath} set, are meant to be used within enclosing math environments,
% hence we don't need to handle them specially, since the numbering and the
% counting is being done on the side of \pkg{amsmath}.  This includes the new
% \env{cases} and \env{matrix} variants, and also \env{multlined}.
%
% Hence, as far as I can tell, the only cross-reference related feature to
% deal with is the \opt{showonlyrefs} option, whose machinery involves writing
% an extra internal label to the \file{.aux} file to track for labels which
% get actually referred to.  This is a little more involved, and implies in
% doing special handling inside \cs{zcref}, but the feature is very cool, so
% it's worth it.
%
%    \begin{macrocode}
\bool_new:N \l_@@_mathtools_showonlyrefs_bool
\@@_compat_module:nn { mathtools }
  {
    \@@_if_package_loaded:nT { mathtools }
      {
        \MH_if_boolean:nT { show_only_refs }
          {
            \bool_set_true:N \l_@@_mathtools_showonlyrefs_bool
            \cs_new_protected:Npn \@@_mathtools_showonlyrefs:n #1
              {
                \@bsphack
                \seq_map_inline:Nn #1
                  {
                    \exp_args:Nx \tl_if_eq:nnTF
                      { \@@_extract_unexp:nnn {##1} { zc@type } { } }
                      { equation }
                      {
                        \protected@write \@auxout { }
                          { \string \MT@newlabel {##1} }
                      }
                      {
                        \exp_args:Nx \tl_if_eq:nnT
                          { \@@_extract_unexp:nnn {##1} { zc@type } { } }
                          { parentequation }
                          {
                            \protected@write \@auxout { }
                              { \string \MT@newlabel {##1} }
                          }
                      }
                  }
                \@esphack
              }
            \msg_info:nnn { zref-clever } { compat-package } { mathtools }
          }
      }
  }
%    \end{macrocode}
%
%
% \subsection{\opt{breqn}}
%
% From the \pkg{breqn} documentation: \textquote{Use of the normal \cs{label}
% command instead of the \opt{label} option works, I think, most of the time
% (untested)}.  Indeed, light testing suggests it does work for \cs{zlabel}
% just as well.  However, if it happens not to work, there was no easy
% alternative handle I could find.  In particular, it does not seem viable to
% leverage the \opt{label=} option without hacking the package internals, even
% if the case of doing so would not be specially tricky, just ``not very
% civil''.
%
%    \begin{macrocode}
\@@_compat_module:nn { breqn }
  {
    \@@_if_package_loaded:nT { breqn }
      {
%    \end{macrocode}
% Contrary to the practice in \pkg{amsmath}, which prints \cs{tag} even in
% unnumbered environments, the starred environments from \pkg{breqn} don't
% typeset any tag/number at all, even for a manually given \opt{number=} as an
% option.  So, even if one can actually set a label in them, it is not really
% meaningful to make a reference to them.  Also contrary to \pkg{amsmath}'s
% practice, \pkg{breqn} uses \cs{stepcounter} instead of \cs{refstepcounter}
% for incrementing the equation counters (see
% \url{https://tex.stackexchange.com/a/241150}).
%    \begin{macrocode}
        \bool_new:N \l_@@_breqn_dgroup_bool
        \AddToHook { env / dgroup / begin }
          {
            \@@_zcsetup:x
              {
                counterresetby =
                  {
                    parentequation =
                      \@@_counter_reset_by:n { equation } ,
                    equation = parentequation ,
                  } ,
                currentcounter = parentequation ,
                countertype = { parentequation = equation } ,
              }
            \bool_set_true:N \l_@@_breqn_dgroup_bool
          }
        \zref@ifpropundefined { subeq }
          { \zref@newprop { subeq } { \alph { equation } } }
          { }
        \clist_map_inline:nn
          {
            dmath ,
            dseries ,
            darray ,
          }
          {
            \AddToHook { env / #1 / begin }
              {
                \@@_zcsetup:n { currentcounter = equation }
                \bool_if:NT \l_@@_breqn_dgroup_bool
                  { \zref@localaddprop \ZREF@mainlist { subeq } }
              }
          }
        \msg_info:nnn { zref-clever } { compat-package } { breqn }
      }
  }
%    \end{macrocode}
%
%
%
% \subsection{\opt{listings}}
%
%    \begin{macrocode}
\@@_compat_module:nn { listings }
  {
    \@@_if_package_loaded:nT { listings }
      {
        \@@_zcsetup:n
          {
            countertype =
              {
                lstlisting = listing ,
                lstnumber = line ,
              } ,
            counterresetby = { lstnumber = lstlisting } ,
          }
%    \end{macrocode}
% Set (also) a \cs{zlabel} with the label received in the \texttt{label=}
% option from the \texttt{lstlisting} environment.  The \emph{only} place to
% set this label is the \texttt{PreInit} hook.  This hook, comes right after
% \cs{lst@MakeCaption} in \cs{lst@Init}, which runs \cs{refstepcounter} on
% \texttt{lstlisting}, so we must come after it.  Also \pkg{listings} itself
% sets \cs{@currentlabel} to \cs{thelstnumber} in the \texttt{Init} hook,
% which comes right after the \texttt{PreInit} one in \cs{lst@Init}.  Since,
% if we add to \texttt{Init} here, we go to the end of it, we'd be seeing the
% wrong \cs{@currentlabel} at that point.
%    \begin{macrocode}
        \lst@AddToHook { PreInit }
          { \tl_if_empty:NF \lst@label { \zlabel { \lst@label } } }
%    \end{macrocode}
% Set \texttt{currentcounter} to \texttt{lstnumber} in the \texttt{Init} hook,
% since \pkg{listings} itself sets \cs{@currentlabel} to \cs{thelstnumber}
% here.  Note that \pkg{listings} \emph{does use} \cs{refstepcounter} on
% \texttt{lstnumber}, but does so in the \texttt{EveryPar} hook, and there
% must be some grouping involved such that \cs{@currentcounter} ends up not
% being visible to the label.  See section ``Line numbers'' of `\texttt{texdoc
% listings-devel}' (the \file{.dtx}), and search for the definition of macro
% \cs{c@lstnumber}.  Indeed, the fact that \pkg{listings} manually sets
% \cs{@currentlabel} to \cs{thelstnumber} is a signal that the work of
% \cs{refstepcounter} is being restrained somehow.
%    \begin{macrocode}
        \lst@AddToHook { Init }
          { \@@_zcsetup:n { currentcounter = lstnumber } }
        \msg_info:nnn { zref-clever } { compat-package } { listings }
      }
  }
%    \end{macrocode}
%
%
% \subsection{\opt{enumitem}}
%
% The procedure below will ``see'' any changes made to the \texttt{enumerate}
% environment (made with \pkg{enumitem}'s \cs{renewlist}) as long as it is
% done in the preamble.  Though, technically, \cs{renewlist} can be issued
% anywhere in the document, this should be more than enough for the purpose at
% hand.  Besides, trying to retrieve this information ``on the fly'' would be
% much overkill.
%
% The only real reason to ``renew'' \texttt{enumerate} itself is to change
% \marg{max-depth}.  \cs{renewlist} \emph{hard-codes} \texttt{max-depth} in
% the environment's definition (well, just as the kernel does), so we cannot
% retrieve this information from any sort of variable.  But \cs{renewlist}
% also creates any needed missing counters, so we can use their existence to
% make the appropriate settings.  In the end, the existence of the counters is
% indeed what matters from \pkg{zref-clever}'s perspective.  Since the first
% four are defined by the kernel and already setup for \pkg{zref-clever} by
% default, we start from \(5\), and stop at the first non-existent
% \cs[no-index]{c@enumN} counter.
%
%    \begin{macrocode}
\@@_compat_module:nn { enumitem }
  {
    \@@_if_package_loaded:nT { enumitem }
      {
        \int_set:Nn \l_tmpa_int { 5 }
        \bool_while_do:nn
          {
            \cs_if_exist_p:c
              { c@ enum \int_to_roman:n { \l_tmpa_int } }
          }
          {
            \@@_zcsetup:x
              {
                counterresetby =
                  {
                    enum \int_to_roman:n { \l_tmpa_int } =
                    enum \int_to_roman:n { \l_tmpa_int - 1 }
                  } ,
                countertype =
                  { enum \int_to_roman:n { \l_tmpa_int } = item } ,
              }
            \int_incr:N \l_tmpa_int
          }
        \int_compare:nNnT { \l_tmpa_int } > { 5 }
          { \msg_info:nnn { zref-clever } { compat-package } { enumitem } }
      }
  }
%    \end{macrocode}
%
%
%
% \subsection{\opt{subcaption}}
%
%
%    \begin{macrocode}
\@@_compat_module:nn { subcaption }
  {
    \@@_if_package_loaded:nT { subcaption }
      {
        \@@_zcsetup:n
          {
            countertype =
              {
                subfigure = figure ,
                subtable = table ,
              } ,
            counterresetby =
              {
                subfigure = figure ,
                subtable = table ,
              } ,
          }
%    \end{macrocode}
% Support for \texttt{subref} reference.
%    \begin{macrocode}
        \zref@newprop { subref }
          { \cs_if_exist_use:c { thesub \@captype } }
        \tl_put_right:Nn \caption@subtypehook
          { \zref@localaddprop \ZREF@mainlist { subref } }
      }
  }
%    \end{macrocode}
%
%
% \subsection{\opt{subfig}}
%
% Though \pkg{subfig} offers \cs{subref} (as \pkg{subcaption}), I could not
% find any reasonable place to add the \texttt{subref} property to
% \pkg{zref}'s main list.
%
%    \begin{macrocode}
\@@_compat_module:nn { subfig }
  {
    \@@_if_package_loaded:nT { subfig }
      {
        \@@_zcsetup:n
          {
            countertype =
              {
                subfigure = figure ,
                subtable = table ,
              } ,
            counterresetby =
              {
                subfigure = figure ,
                subtable = table ,
              } ,
          }
      }
  }
%    \end{macrocode}
%
%
%    \begin{macrocode}
%</package>
%    \end{macrocode}
%
%
%
% \section{Language files}
%
% Initial values for the English, German, French, Portuguese, and Spanish
% language files have been provided by the author.  Translations available for
% document elements' names in other packages have been an useful reference for
% the purpose, namely: \pkg{babel}, \pkg{cleveref}, \pkg{translator}, and
% \pkg{translations}.
%
%
% \subsection{Localization guidelines}
%
% Since the task of localizing \pkg{zref-clever} to work in different
% languages depends on the generous work of contributors, it is a good idea to
% set some guidelines not only to ease the task itself but also to document
% what the package expects in this regard.
%
% The first general observation is that, contrary to a common initial reaction
% of those faced with the task of localizing the reference types, is that the
% job is not quite one of ``translation''.  The reference type names are just
% the internal names used by the package to refer to them, technically, they
% could just as well be foobars.  Of course, for practical reasons, they were
% chosen to be semantic.  However, what we are searching for is not really the
% translation to the reference type name itself, but rather for the word /
% term / expression which is typically used to refer to the document object
% that the reference type is meant to represent.  And terms that should work
% well in the contexts which cross-references are commonly used.
%
% That said, some comments about the reference types and common pitfalls.
%
% \textbf{Sectioning:} A number of reference types are provided to support
% referencing to document sectioning commands.  Obviously, \texttt{part},
% \texttt{chapter}, \texttt{section}, and \texttt{paragraph} are meant to
% refer to the sectioning commands of the standard classes and elsewhere,
% which anyone reading this is certainly acquainted with.  Note that
% \pkg{zref-clever} uses -- by default at least, which is what the language
% files cater for -- the \texttt{section} reference type to refer to
% \cs{subsections} and \cs{subsubsections} as well, similarly,
% \texttt{paragraph} also refers to \cs{subparagraph}.  The \texttt{appendix}
% reference type is meant to refer to any sectioning command -- be them
% chapters, sections, or paragraphs -- issued after \cs{appendix}, which
% corresponds to how the standard classes, the KOMA Script classes, and
% \cls{memoir} deal with appendices.  The \texttt{book} reference type
% deserves some explanation.  The word ``book'' has a good number of meanings,
% and the most common one is not the one which is intended here.  The Webster
% dictionary gives us a couple of definitions of interest: ``1. A collection
% of sheets of paper, or similar material, blank, written, or printed, bound
% together; commonly, many folded and bound sheets containing continuous
% printing or writing.'' and ``3. A part or subdivision of a treatise or
% literary work; as, the tenth book of `Paradise Lost'.''  It is this third
% meaning which the \texttt{book} reference type is meant to support: a major
% subdivision of a work, much like \cs{part}.  Even if it does not exist in
% the standard classes, it may exist elsewhere, in particular, it is provided
% by \cls{memoir}.
%
% \textbf{Common numbered objects:} Nothing surprising here, just being
% explicit.  \texttt{table} and \texttt{figure} refer to the document's
% respective floats objects.  \texttt{page} to the page number.  \texttt{item}
% to the item number in \env{enumerate} environments.  Similarly,
% \texttt{line} is meant to refer to line numbers.
%
% \textbf{Notes:} \pkg{zref-clever} provides three reference types in this
% area: \texttt{footnote}, \texttt{endnote}, and \texttt{note}.  The first two
% refer to footnotes and end notes, respectively.  The third is meant as a
% convenience for a general ``note'' object, either the other two, or
% something else.  By experience, here is one place where that initial
% observation of not simply translating the reference types names is
% particularly relevant.  There's a natural temptation, because three
% different types exist and are somewhat close to each other, to distinguish
% them clearly.  Duty would compel us to do so.  But that may lead to less
% than ideal results.  Different terms work well for some languages, like
% English and German, which have compound words for the purpose.  But less so
% for other languages, like Portuguese, French, or Italian.  For example, in a
% document in French which only contains footnotes, arguably a very common use
% case, would it be better to refer to a footnote as just ``note'', or be very
% precise with ``note infrapaginale''?  Of course, in a document which
% contains both footnotes and end notes, we may need the distinction.  But is
% it really the better default?  True, possibly the inclusion of the
% \texttt{note} reference type, with no clear object to refer to, creates more
% noise than convenience here.  If I recall correctly, my intention was to
% provide an easy way out for users from possible contentious localizations
% for \texttt{footnote} and \texttt{endnote}, but I'm not sure if it's been
% working like this in practice, and I should probably have refrained from
% adding it in the first place.
%
% \textbf{Math \& Co.:} A good number of reference types provided by the
% package are meant to cater for document objects commonly used in Mathematics
% and related areas.  They are either straight math environments, defined by
% the kernel, \pkg{amsmath} or other packages, or environments which are
% normally not pre-defined by the kernel or the standard classes, but are
% traditionally defined by users with the kernel's \cs{newtheorem} or similar
% constructs available in the \LaTeX{} package ecosystem.  For most of them,
% localization should strive as much as possible to use the formal terms,
% jargon really, typically employed by mathematicians, logicians, and friends.
% Namely for the reference types: \texttt{equation}, \texttt{theorem},
% \texttt{lemma}, \texttt{corollary}, \texttt{proposition},
% \texttt{definition}, \texttt{proof}, \texttt{result}, and \texttt{remark}.
% Regarding \texttt{example}, \texttt{exercise}, and \texttt{solution} being
% somewhat less formal is admissible.  But the chosen terms should still be
% fit for use in Math related contexts, and should be assumed were created by
% \cs{newtheorem} or similar, even if users may well find other uses for these
% types.
%
% \textbf{Code:} A couple of reference types are provided for code related
% environments: \texttt{algorithm} and \texttt{listing}.  By experience, the
% \texttt{listing} type has already proven to be a particularly challenging
% one.  Formally, it should be a good default term to encompass anything which
% may regularly be included in a \env{lstlisting} environment as provided by
% the \pkg{listings} package.  However, it seems that in different languages
% it is quite difficult to find a satisfying term for it.  Though my English
% is decent, I'm not a native speaker, still I'm not even sure how common the
% term is used for the purpose even in English.  It seems to be traditional
% enough in the \LaTeX{} community at least.  In doubt, pend to the jargon
% side, anglicism if need be.  Since we are bound to displease mostly everyone
% anyway, at least we do so in a consistent manner.
%
% \textbf{Completeness and abbreviated forms:} Ideally, the language file
% should be as complete as possible.  ``Complete'' meaning it contains: i) the
% defaults for all basic separators, \opt{namesep}, \opt{pairsep},
% \opt{listsep}, \opt{lastsep}, \opt{tpairsep}, \opt{tlistsep},
% \opt{tlastsep}, \opt{notesep}, and \opt{rangesep}; ii) the non-abbreviated
% forms of names for all the supported reference types, according to the
% language definitions, that is, usually for \opt{Name-sg}, \opt{name-sg},
% \opt{Name-pl}, \opt{name-pl}, but only for the capitalized forms if the
% language was declared with \opt{allcaps} option, and names for each
% declension case, if the language was declared with \opt{declension}; iii)
% genders for each reference type, if the language was declared with
% \opt{gender}.  The language file may include some other things, like some
% type specific settings for separators or refbounds, and also some
% abbreviated name forms.  In the case of abbreviated name forms, it is usual
% and desirable to provide some, but they should be used sparingly, only for
% cases where the abbreviation is a common and well established tradition for
% the language.  The reason is that \texttt{abbrev=true} is quite a common use
% case, and it is easier to provide an occasional wanted abbreviated form, if
% the language file didn't include it, than it is to disable several unwanted
% ones, if the language file includes too many of them.  What should be aimed
% at is to provide a good default abbreviations set.  Unusual or disputable
% abbreviations should be avoided.  In particular, there is no need at all to
% provide the same set of abbreviations for each language.  It is not because
% English has them for a given type that some other language has to have them,
% and it is not because English lacks them for another type, that other
% languages shouldn't have them.  Still, with regard to abbreviated forms, it
% is better to be conservative than opinionated.
%
% \textbf{\pkg{babel} names:} As is known, \pkg{babel} defines a set of
% captions for different document objects for each supported language.  In
% some cases, they intersect with the objects referred to with
% cross-references, in which case consistency with \pkg{babel} should be
% maintained as much as possible.  This is specially the case for prominent
% and traditional objects, such as \cs{chaptername}, \cs{figurename},
% \cs{tablename}, \cs{pagename}, \cs{partname}, and \cs{appendixname}.  This
% is not set in stone, but there should be good reason to diverge from it.  In
% particular, if a certain term is contentious in a given language,
% \pkg{babel}'s default should be preferred.  For example, ``table''
% vs. ``tableau'' in French, or ``cuadro'' vs. ``tabla'' in Spanish.
%
% \textbf{Input encoding of language files:} When \pkg{zref-clever} was
% released, the \LaTeX{} kernel already used UTF-8 as default input encoding.
% Indeed, \pkg{zref-clever} requires a kernel even newer than the one where
% the default input encoding was changed.  That given, UTF-8 input encoding
% was made a requirement of the package, and hence the language files should
% be in UTF-8, since it makes them easier to read and maintain than LICR.
%
% \textbf{Precedence rule for options in the language files:} Any option given
% twice or more times has to have some precedence rule.  Normally, the
% language files should not contain options in duplicity, but they may happen
% when setting some ``group'' \opt{refbounds} options, in which case
% precedence rules become relevant.  For user facing options (those set with
% \cs{zcLanguageSetup}), the option is always set, regardless of its previous
% state.  Which means that the last value takes precedence.  For the language
% files, we have to load them at \texttt{begindocument} (or later), since
% that's the point where we know from \pkg{babel} or \pkg{polyglossia} the
% \cs{languagename}.  But we also don't want to override any options the user
% has actively set in the preamble.  So the language files only set the values
% if they were not previously set.  In other words, for them the precedence
% order is inverted, the first value takes precedence.
%
% \textbf{\pkg{zref-vario}:} If you are interested in the localization of
% \pkg{zref-clever} to your language, and willing to contribute to it, you may
% also want to consider doing the same for the companion package
% \pkg{zref-vario}.  It is actually a much simpler task than localizing
% \pkg{zref-clever}.
%
%
% \subsection{English}
%
% English language file has been initially provided by the author.
%
%    \begin{macrocode}
%<*package>
\zcDeclareLanguage { english }
\zcDeclareLanguageAlias { american   } { english }
\zcDeclareLanguageAlias { australian } { english }
\zcDeclareLanguageAlias { british    } { english }
\zcDeclareLanguageAlias { canadian   } { english }
\zcDeclareLanguageAlias { newzealand } { english }
\zcDeclareLanguageAlias { UKenglish  } { english }
\zcDeclareLanguageAlias { USenglish  } { english }
%</package>
%    \end{macrocode}
%
%    \begin{macrocode}
%<*lang-english>
%    \end{macrocode}
%
%    \begin{macrocode}
namesep   = {\nobreakspace} ,
pairsep   = {~and\nobreakspace} ,
listsep   = {,~} ,
lastsep   = {~and\nobreakspace} ,
tpairsep  = {~and\nobreakspace} ,
tlistsep  = {,~} ,
tlastsep  = {,~and\nobreakspace} ,
notesep   = {~} ,
rangesep  = {~to\nobreakspace} ,

type = book ,
  Name-sg = Book ,
  name-sg = book ,
  Name-pl = Books ,
  name-pl = books ,

type = part ,
  Name-sg = Part ,
  name-sg = part ,
  Name-pl = Parts ,
  name-pl = parts ,

type = chapter ,
  Name-sg = Chapter ,
  name-sg = chapter ,
  Name-pl = Chapters ,
  name-pl = chapters ,

type = section ,
  Name-sg = Section ,
  name-sg = section ,
  Name-pl = Sections ,
  name-pl = sections ,

type = paragraph ,
  Name-sg = Paragraph ,
  name-sg = paragraph ,
  Name-pl = Paragraphs ,
  name-pl = paragraphs ,
  Name-sg-ab = Par. ,
  name-sg-ab = par. ,
  Name-pl-ab = Par. ,
  name-pl-ab = par. ,

type = appendix ,
  Name-sg = Appendix ,
  name-sg = appendix ,
  Name-pl = Appendices ,
  name-pl = appendices ,

type = page ,
  Name-sg = Page ,
  name-sg = page ,
  Name-pl = Pages ,
  name-pl = pages ,
  rangesep = {\textendash} ,
  rangetopair = false ,

type = line ,
  Name-sg = Line ,
  name-sg = line ,
  Name-pl = Lines ,
  name-pl = lines ,

type = figure ,
  Name-sg = Figure ,
  name-sg = figure ,
  Name-pl = Figures ,
  name-pl = figures ,
  Name-sg-ab = Fig. ,
  name-sg-ab = fig. ,
  Name-pl-ab = Figs. ,
  name-pl-ab = figs. ,

type = table ,
  Name-sg = Table ,
  name-sg = table ,
  Name-pl = Tables ,
  name-pl = tables ,

type = item ,
  Name-sg = Item ,
  name-sg = item ,
  Name-pl = Items ,
  name-pl = items ,

type = footnote ,
  Name-sg = Footnote ,
  name-sg = footnote ,
  Name-pl = Footnotes ,
  name-pl = footnotes ,

type = endnote ,
  Name-sg = Note ,
  name-sg = note ,
  Name-pl = Notes ,
  name-pl = notes ,

type = note ,
  Name-sg = Note ,
  name-sg = note ,
  Name-pl = Notes ,
  name-pl = notes ,

type = equation ,
  Name-sg = Equation ,
  name-sg = equation ,
  Name-pl = Equations ,
  name-pl = equations ,
  Name-sg-ab = Eq. ,
  name-sg-ab = eq. ,
  Name-pl-ab = Eqs. ,
  name-pl-ab = eqs. ,
  refbounds-first-sg = {,(,),} ,
  refbounds = {(,,,)} ,

type = theorem ,
  Name-sg = Theorem ,
  name-sg = theorem ,
  Name-pl = Theorems ,
  name-pl = theorems ,

type = lemma ,
  Name-sg = Lemma ,
  name-sg = lemma ,
  Name-pl = Lemmas ,
  name-pl = lemmas ,

type = corollary ,
  Name-sg = Corollary ,
  name-sg = corollary ,
  Name-pl = Corollaries ,
  name-pl = corollaries ,

type = proposition ,
  Name-sg = Proposition ,
  name-sg = proposition ,
  Name-pl = Propositions ,
  name-pl = propositions ,

type = definition ,
  Name-sg = Definition ,
  name-sg = definition ,
  Name-pl = Definitions ,
  name-pl = definitions ,

type = proof ,
  Name-sg = Proof ,
  name-sg = proof ,
  Name-pl = Proofs ,
  name-pl = proofs ,

type = result ,
  Name-sg = Result ,
  name-sg = result ,
  Name-pl = Results ,
  name-pl = results ,

type = remark ,
  Name-sg = Remark ,
  name-sg = remark ,
  Name-pl = Remarks ,
  name-pl = remarks ,

type = example ,
  Name-sg = Example ,
  name-sg = example ,
  Name-pl = Examples ,
  name-pl = examples ,

type = algorithm ,
  Name-sg = Algorithm ,
  name-sg = algorithm ,
  Name-pl = Algorithms ,
  name-pl = algorithms ,

type = listing ,
  Name-sg = Listing ,
  name-sg = listing ,
  Name-pl = Listings ,
  name-pl = listings ,

type = exercise ,
  Name-sg = Exercise ,
  name-sg = exercise ,
  Name-pl = Exercises ,
  name-pl = exercises ,

type = solution ,
  Name-sg = Solution ,
  name-sg = solution ,
  Name-pl = Solutions ,
  name-pl = solutions ,
%    \end{macrocode}
%
%    \begin{macrocode}
%</lang-english>
%    \end{macrocode}
%
%
%
% \subsection{German}
%
% German language file has been initially provided by the author.
%
% \pkg{babel-german} also has \file{.ldf}s for \texttt{germanb} and
% \texttt{ngermanb}, but they are deprecated as options and, if used, they
% fall back respectively to \texttt{german} and \texttt{ngerman}.
%
%    \begin{macrocode}
%<*package>
\zcDeclareLanguage
  [ declension = { N , A , D , G } , gender = { f , m , n } , allcaps ]
  { german }
\zcDeclareLanguageAlias { ngerman      } { german }
\zcDeclareLanguageAlias { austrian     } { german }
\zcDeclareLanguageAlias { naustrian    } { german }
\zcDeclareLanguageAlias { swissgerman  } { german }
\zcDeclareLanguageAlias { nswissgerman } { german }
%</package>
%    \end{macrocode}
%
%    \begin{macrocode}
%<*lang-german>
%    \end{macrocode}
%
%    \begin{macrocode}
namesep  = {\nobreakspace} ,
pairsep  = {~und\nobreakspace} ,
listsep  = {,~} ,
lastsep  = {~und\nobreakspace} ,
tpairsep = {~und\nobreakspace} ,
tlistsep = {,~} ,
tlastsep = {~und\nobreakspace} ,
notesep  = {~} ,
rangesep = {~bis\nobreakspace} ,

type = book ,
  gender = n ,
  case = N ,
    Name-sg = Buch ,
    Name-pl = Bücher ,
  case = A ,
    Name-sg = Buch ,
    Name-pl = Bücher ,
  case = D ,
    Name-sg = Buch ,
    Name-pl = Büchern ,
  case = G ,
    Name-sg = Buches ,
    Name-pl = Bücher ,

type = part ,
  gender = m ,
  case = N ,
    Name-sg = Teil ,
    Name-pl = Teile ,
  case = A ,
    Name-sg = Teil ,
    Name-pl = Teile ,
  case = D ,
    Name-sg = Teil ,
    Name-pl = Teilen ,
  case = G ,
    Name-sg = Teiles ,
    Name-pl = Teile ,

type = chapter ,
  gender = n ,
  case = N ,
    Name-sg = Kapitel ,
    Name-pl = Kapitel ,
  case = A ,
    Name-sg = Kapitel ,
    Name-pl = Kapitel ,
  case = D ,
    Name-sg = Kapitel ,
    Name-pl = Kapiteln ,
  case = G ,
    Name-sg = Kapitels ,
    Name-pl = Kapitel ,

type = section ,
  gender = m ,
  case = N ,
    Name-sg = Abschnitt ,
    Name-pl = Abschnitte ,
  case = A ,
    Name-sg = Abschnitt ,
    Name-pl = Abschnitte ,
  case = D ,
    Name-sg = Abschnitt ,
    Name-pl = Abschnitten ,
  case = G ,
    Name-sg = Abschnitts ,
    Name-pl = Abschnitte ,

type = paragraph ,
  gender = m ,
  case = N ,
    Name-sg = Absatz ,
    Name-pl = Absätze ,
  case = A ,
    Name-sg = Absatz ,
    Name-pl = Absätze ,
  case = D ,
    Name-sg = Absatz ,
    Name-pl = Absätzen ,
  case = G ,
    Name-sg = Absatzes ,
    Name-pl = Absätze ,

type = appendix ,
  gender = m ,
  case = N ,
    Name-sg = Anhang ,
    Name-pl = Anhänge ,
  case = A ,
    Name-sg = Anhang ,
    Name-pl = Anhänge ,
  case = D ,
    Name-sg = Anhang ,
    Name-pl = Anhängen ,
  case = G ,
    Name-sg = Anhangs ,
    Name-pl = Anhänge ,

type = page ,
  gender = f ,
  case = N ,
    Name-sg = Seite ,
    Name-pl = Seiten ,
  case = A ,
    Name-sg = Seite ,
    Name-pl = Seiten ,
  case = D ,
    Name-sg = Seite ,
    Name-pl = Seiten ,
  case = G ,
    Name-sg = Seite ,
    Name-pl = Seiten ,
  rangesep = {\textendash} ,
  rangetopair = false ,

type = line ,
  gender = f ,
  case = N ,
    Name-sg = Zeile ,
    Name-pl = Zeilen ,
  case = A ,
    Name-sg = Zeile ,
    Name-pl = Zeilen ,
  case = D ,
    Name-sg = Zeile ,
    Name-pl = Zeilen ,
  case = G ,
    Name-sg = Zeile ,
    Name-pl = Zeilen ,

type = figure ,
  gender = f ,
  case = N ,
    Name-sg = Abbildung ,
    Name-pl = Abbildungen ,
    Name-sg-ab = Abb. ,
    Name-pl-ab = Abb. ,
  case = A ,
    Name-sg = Abbildung ,
    Name-pl = Abbildungen ,
    Name-sg-ab = Abb. ,
    Name-pl-ab = Abb. ,
  case = D ,
    Name-sg = Abbildung ,
    Name-pl = Abbildungen ,
    Name-sg-ab = Abb. ,
    Name-pl-ab = Abb. ,
  case = G ,
    Name-sg = Abbildung ,
    Name-pl = Abbildungen ,
    Name-sg-ab = Abb. ,
    Name-pl-ab = Abb. ,

type = table ,
  gender = f ,
  case = N ,
    Name-sg = Tabelle ,
    Name-pl = Tabellen ,
  case = A ,
    Name-sg = Tabelle ,
    Name-pl = Tabellen ,
  case = D ,
    Name-sg = Tabelle ,
    Name-pl = Tabellen ,
  case = G ,
    Name-sg = Tabelle ,
    Name-pl = Tabellen ,

type = item ,
  gender = m ,
  case = N ,
    Name-sg = Punkt ,
    Name-pl = Punkte ,
  case = A ,
    Name-sg = Punkt ,
    Name-pl = Punkte ,
  case = D ,
    Name-sg = Punkt ,
    Name-pl = Punkten ,
  case = G ,
    Name-sg = Punktes ,
    Name-pl = Punkte ,

type = footnote ,
  gender = f ,
  case = N ,
    Name-sg = Fußnote ,
    Name-pl = Fußnoten ,
  case = A ,
    Name-sg = Fußnote ,
    Name-pl = Fußnoten ,
  case = D ,
    Name-sg = Fußnote ,
    Name-pl = Fußnoten ,
  case = G ,
    Name-sg = Fußnote ,
    Name-pl = Fußnoten ,

type = endnote ,
  gender = f ,
  case = N ,
    Name-sg = Endnote ,
    Name-pl = Endnoten ,
  case = A ,
    Name-sg = Endnote ,
    Name-pl = Endnoten ,
  case = D ,
    Name-sg = Endnote ,
    Name-pl = Endnoten ,
  case = G ,
    Name-sg = Endnote ,
    Name-pl = Endnoten ,

type = note ,
  gender = f ,
  case = N ,
    Name-sg = Anmerkung ,
    Name-pl = Anmerkungen ,
  case = A ,
    Name-sg = Anmerkung ,
    Name-pl = Anmerkungen ,
  case = D ,
    Name-sg = Anmerkung ,
    Name-pl = Anmerkungen ,
  case = G ,
    Name-sg = Anmerkung ,
    Name-pl = Anmerkungen ,

type = equation ,
  gender = f ,
  case = N ,
    Name-sg = Gleichung ,
    Name-pl = Gleichungen ,
  case = A ,
    Name-sg = Gleichung ,
    Name-pl = Gleichungen ,
  case = D ,
    Name-sg = Gleichung ,
    Name-pl = Gleichungen ,
  case = G ,
    Name-sg = Gleichung ,
    Name-pl = Gleichungen ,
  refbounds-first-sg = {,(,),} ,
  refbounds = {(,,,)} ,

type = theorem ,
  gender = n ,
  case = N ,
    Name-sg = Theorem ,
    Name-pl = Theoreme ,
  case = A ,
    Name-sg = Theorem ,
    Name-pl = Theoreme ,
  case = D ,
    Name-sg = Theorem ,
    Name-pl = Theoremen ,
  case = G ,
    Name-sg = Theorems ,
    Name-pl = Theoreme ,

type = lemma ,
  gender = n ,
  case = N ,
    Name-sg = Lemma ,
    Name-pl = Lemmata ,
  case = A ,
    Name-sg = Lemma ,
    Name-pl = Lemmata ,
  case = D ,
    Name-sg = Lemma ,
    Name-pl = Lemmata ,
  case = G ,
    Name-sg = Lemmas ,
    Name-pl = Lemmata ,

type = corollary ,
  gender = n ,
  case = N ,
    Name-sg = Korollar ,
    Name-pl = Korollare ,
  case = A ,
    Name-sg = Korollar ,
    Name-pl = Korollare ,
  case = D ,
    Name-sg = Korollar ,
    Name-pl = Korollaren ,
  case = G ,
    Name-sg = Korollars ,
    Name-pl = Korollare ,

type = proposition ,
  gender = m ,
  case = N ,
    Name-sg = Satz ,
    Name-pl = Sätze ,
  case = A ,
    Name-sg = Satz ,
    Name-pl = Sätze ,
  case = D ,
    Name-sg = Satz ,
    Name-pl = Sätzen ,
  case = G ,
    Name-sg = Satzes ,
    Name-pl = Sätze ,

type = definition ,
  gender = f ,
  case = N ,
    Name-sg = Definition ,
    Name-pl = Definitionen ,
  case = A ,
    Name-sg = Definition ,
    Name-pl = Definitionen ,
  case = D ,
    Name-sg = Definition ,
    Name-pl = Definitionen ,
  case = G ,
    Name-sg = Definition ,
    Name-pl = Definitionen ,

type = proof ,
  gender = m ,
  case = N ,
    Name-sg = Beweis ,
    Name-pl = Beweise ,
  case = A ,
    Name-sg = Beweis ,
    Name-pl = Beweise ,
  case = D ,
    Name-sg = Beweis ,
    Name-pl = Beweisen ,
  case = G ,
    Name-sg = Beweises ,
    Name-pl = Beweise ,

type = result ,
  gender = n ,
  case = N ,
    Name-sg = Ergebnis ,
    Name-pl = Ergebnisse ,
  case = A ,
    Name-sg = Ergebnis ,
    Name-pl = Ergebnisse ,
  case = D ,
    Name-sg = Ergebnis ,
    Name-pl = Ergebnissen ,
  case = G ,
    Name-sg = Ergebnisses ,
    Name-pl = Ergebnisse ,

type = remark ,
  gender = f ,
  case = N ,
    Name-sg = Bemerkung ,
    Name-pl = Bemerkungen ,
  case = A ,
    Name-sg = Bemerkung ,
    Name-pl = Bemerkungen ,
  case = D ,
    Name-sg = Bemerkung ,
    Name-pl = Bemerkungen ,
  case = G ,
    Name-sg = Bemerkung ,
    Name-pl = Bemerkungen ,

type = example ,
  gender = n ,
  case = N ,
    Name-sg = Beispiel ,
    Name-pl = Beispiele ,
  case = A ,
    Name-sg = Beispiel ,
    Name-pl = Beispiele ,
  case = D ,
    Name-sg = Beispiel ,
    Name-pl = Beispielen ,
  case = G ,
    Name-sg = Beispiels ,
    Name-pl = Beispiele ,

type = algorithm ,
  gender = m ,
  case = N ,
    Name-sg = Algorithmus ,
    Name-pl = Algorithmen ,
  case = A ,
    Name-sg = Algorithmus ,
    Name-pl = Algorithmen ,
  case = D ,
    Name-sg = Algorithmus ,
    Name-pl = Algorithmen ,
  case = G ,
    Name-sg = Algorithmus ,
    Name-pl = Algorithmen ,

type = listing ,
  gender = n ,
  case = N ,
    Name-sg = Listing ,
    Name-pl = Listings ,
  case = A ,
    Name-sg = Listing ,
    Name-pl = Listings ,
  case = D ,
    Name-sg = Listing ,
    Name-pl = Listings ,
  case = G ,
    Name-sg = Listings ,
    Name-pl = Listings ,

type = exercise ,
  gender = f ,
  case = N ,
    Name-sg = Übungsaufgabe ,
    Name-pl = Übungsaufgaben ,
  case = A ,
    Name-sg = Übungsaufgabe ,
    Name-pl = Übungsaufgaben ,
  case = D ,
    Name-sg = Übungsaufgabe ,
    Name-pl = Übungsaufgaben ,
  case = G ,
    Name-sg = Übungsaufgabe ,
    Name-pl = Übungsaufgaben ,

type = solution ,
  gender = f ,
  case = N ,
    Name-sg = Lösung ,
    Name-pl = Lösungen ,
  case = A ,
    Name-sg = Lösung ,
    Name-pl = Lösungen ,
  case = D ,
    Name-sg = Lösung ,
    Name-pl = Lösungen ,
  case = G ,
    Name-sg = Lösung ,
    Name-pl = Lösungen ,
%    \end{macrocode}
%
%    \begin{macrocode}
%</lang-german>
%    \end{macrocode}
%
%
%
% \subsection{French}
%
% French language file has been initially provided by the author, and has been
% improved thanks to \contributor{Denis Bitouzé} and \contributor{François
% Lagarde} (at \githubissue{1}) and participants of the Groupe francophone des
% Utilisateurs de \TeX{} (GUTenberg) (at
% \url{https://groups.google.com/g/gut_fr/c/rNLm6weGcyg}) and the
% \texttt{fr.comp.text.tex} (at
% \url{https://groups.google.com/g/fr.comp.text.tex/c/Fa11Tf6MFFs}) mailing
% lists.
%
% \pkg{babel-french} also has \file{.ldf}s for \texttt{francais},
% \texttt{frenchb}, and \texttt{canadien}, but they are deprecated as options
% and, if used, they fall back to either \texttt{french} or \texttt{acadian}.
%
%    \begin{macrocode}
%<*package>
\zcDeclareLanguage [ gender = { f , m } ] { french }
\zcDeclareLanguageAlias { acadian  } { french }
%</package>
%    \end{macrocode}
%
%    \begin{macrocode}
%<*lang-french>
%    \end{macrocode}
%
%    \begin{macrocode}
namesep  = {\nobreakspace} ,
pairsep  = {~et\nobreakspace} ,
listsep  = {,~} ,
lastsep  = {~et\nobreakspace} ,
tpairsep = {~et\nobreakspace} ,
tlistsep = {,~} ,
tlastsep = {~et\nobreakspace} ,
notesep  = {~} ,
rangesep = {~à\nobreakspace} ,

type = book ,
  gender = m ,
  Name-sg = Livre ,
  name-sg = livre ,
  Name-pl = Livres ,
  name-pl = livres ,

type = part ,
  gender = f ,
  Name-sg = Partie ,
  name-sg = partie ,
  Name-pl = Parties ,
  name-pl = parties ,

type = chapter ,
  gender = m ,
  Name-sg = Chapitre ,
  name-sg = chapitre ,
  Name-pl = Chapitres ,
  name-pl = chapitres ,

type = section ,
  gender = f ,
  Name-sg = Section ,
  name-sg = section ,
  Name-pl = Sections ,
  name-pl = sections ,

type = paragraph ,
  gender = m ,
  Name-sg = Paragraphe ,
  name-sg = paragraphe ,
  Name-pl = Paragraphes ,
  name-pl = paragraphes ,

type = appendix ,
  gender = f ,
  Name-sg = Annexe ,
  name-sg = annexe ,
  Name-pl = Annexes ,
  name-pl = annexes ,

type = page ,
  gender = f ,
  Name-sg = Page ,
  name-sg = page ,
  Name-pl = Pages ,
  name-pl = pages ,
  rangesep = {-} ,
  rangetopair = false ,

type = line ,
  gender = f ,
  Name-sg = Ligne ,
  name-sg = ligne ,
  Name-pl = Lignes ,
  name-pl = lignes ,

type = figure ,
  gender = f ,
  Name-sg = Figure ,
  name-sg = figure ,
  Name-pl = Figures ,
  name-pl = figures ,

type = table ,
  gender = f ,
  Name-sg = Table ,
  name-sg = table ,
  Name-pl = Tables ,
  name-pl = tables ,

type = item ,
  gender = m ,
  Name-sg = Point ,
  name-sg = point ,
  Name-pl = Points ,
  name-pl = points ,

type = footnote ,
  gender = f ,
  Name-sg = Note ,
  name-sg = note ,
  Name-pl = Notes ,
  name-pl = notes ,

type = endnote ,
  gender = f ,
  Name-sg = Note ,
  name-sg = note ,
  Name-pl = Notes ,
  name-pl = notes ,

type = note ,
  gender = f ,
  Name-sg = Note ,
  name-sg = note ,
  Name-pl = Notes ,
  name-pl = notes ,

type = equation ,
  gender = f ,
  Name-sg = Équation ,
  name-sg = équation ,
  Name-pl = Équations ,
  name-pl = équations ,
  refbounds-first-sg = {,(,),} ,
  refbounds = {(,,,)} ,

type = theorem ,
  gender = m ,
  Name-sg = Théorème ,
  name-sg = théorème ,
  Name-pl = Théorèmes ,
  name-pl = théorèmes ,

type = lemma ,
  gender = m ,
  Name-sg = Lemme ,
  name-sg = lemme ,
  Name-pl = Lemmes ,
  name-pl = lemmes ,

type = corollary ,
  gender = m ,
  Name-sg = Corollaire ,
  name-sg = corollaire ,
  Name-pl = Corollaires ,
  name-pl = corollaires ,

type = proposition ,
  gender = f ,
  Name-sg = Proposition ,
  name-sg = proposition ,
  Name-pl = Propositions ,
  name-pl = propositions ,

type = definition ,
  gender = f ,
  Name-sg = Définition ,
  name-sg = définition ,
  Name-pl = Définitions ,
  name-pl = définitions ,

type = proof ,
  gender = f ,
  Name-sg = Démonstration ,
  name-sg = démonstration ,
  Name-pl = Démonstrations ,
  name-pl = démonstrations ,

type = result ,
  gender = m ,
  Name-sg = Résultat ,
  name-sg = résultat ,
  Name-pl = Résultats ,
  name-pl = résultats ,

type = remark ,
  gender = f ,
  Name-sg = Remarque ,
  name-sg = remarque ,
  Name-pl = Remarques ,
  name-pl = remarques ,

type = example ,
  gender = m ,
  Name-sg = Exemple ,
  name-sg = exemple ,
  Name-pl = Exemples ,
  name-pl = exemples ,

type = algorithm ,
  gender = m ,
  Name-sg = Algorithme ,
  name-sg = algorithme ,
  Name-pl = Algorithmes ,
  name-pl = algorithmes ,

type = listing ,
  gender = m ,
  Name-sg = Listing ,
  name-sg = listing ,
  Name-pl = Listings ,
  name-pl = listings ,

type = exercise ,
  gender = m ,
  Name-sg = Exercice ,
  name-sg = exercice ,
  Name-pl = Exercices ,
  name-pl = exercices ,

type = solution ,
  gender = f ,
  Name-sg = Solution ,
  name-sg = solution ,
  Name-pl = Solutions ,
  name-pl = solutions ,
%    \end{macrocode}
%
%    \begin{macrocode}
%</lang-french>
%    \end{macrocode}
%
%
%
% \subsection{Portuguese}
%
% Portuguese language file provided by the author, who's a native speaker of
% (Brazilian) Portuguese.  I do expect this to be sufficiently general, but if
% Portuguese speakers from other places feel the need for a Portuguese
% variant, please let me know.
%
%    \begin{macrocode}
%<*package>
\zcDeclareLanguage [ gender = { f , m } ] { portuguese }
\zcDeclareLanguageAlias { brazilian } { portuguese }
\zcDeclareLanguageAlias { brazil    } { portuguese }
\zcDeclareLanguageAlias { portuges  } { portuguese }
%</package>
%    \end{macrocode}
%
%    \begin{macrocode}
%<*lang-portuguese>
%    \end{macrocode}
%
%    \begin{macrocode}
namesep  = {\nobreakspace} ,
pairsep  = {~e\nobreakspace} ,
listsep  = {,~} ,
lastsep  = {~e\nobreakspace} ,
tpairsep = {~e\nobreakspace} ,
tlistsep = {,~} ,
tlastsep = {~e\nobreakspace} ,
notesep  = {~} ,
rangesep = {~a\nobreakspace} ,

type = book ,
  gender = m ,
  Name-sg =  Livro ,
  name-sg =  livro ,
  Name-pl =  Livros ,
  name-pl =  livros ,

type = part ,
  gender = f ,
  Name-sg = Parte ,
  name-sg = parte ,
  Name-pl = Partes ,
  name-pl = partes ,

type = chapter ,
  gender = m ,
  Name-sg = Capítulo ,
  name-sg = capítulo ,
  Name-pl = Capítulos ,
  name-pl = capítulos ,

type = section ,
  gender = f ,
  Name-sg = Seção ,
  name-sg = seção ,
  Name-pl = Seções ,
  name-pl = seções ,

type = paragraph ,
  gender = m ,
  Name-sg = Parágrafo ,
  name-sg = parágrafo ,
  Name-pl = Parágrafos ,
  name-pl = parágrafos ,
  Name-sg-ab = Par. ,
  name-sg-ab = par. ,
  Name-pl-ab = Par. ,
  name-pl-ab = par. ,

type = appendix ,
  gender = m ,
  Name-sg = Apêndice ,
  name-sg = apêndice ,
  Name-pl = Apêndices ,
  name-pl = apêndices ,

type = page ,
  gender = f ,
  Name-sg = Página ,
  name-sg = página ,
  Name-pl = Páginas ,
  name-pl = páginas ,
  rangesep = {\textendash} ,
  rangetopair = false ,

type = line ,
  gender = f ,
  Name-sg = Linha ,
  name-sg = linha ,
  Name-pl = Linhas ,
  name-pl = linhas ,

type = figure ,
  gender = f ,
  Name-sg = Figura ,
  name-sg = figura ,
  Name-pl = Figuras ,
  name-pl = figuras ,
  Name-sg-ab = Fig. ,
  name-sg-ab = fig. ,
  Name-pl-ab = Figs. ,
  name-pl-ab = figs. ,

type = table ,
  gender = f ,
  Name-sg = Tabela ,
  name-sg = tabela ,
  Name-pl = Tabelas ,
  name-pl = tabelas ,

type = item ,
  gender = m ,
  Name-sg = Item ,
  name-sg = item ,
  Name-pl = Itens ,
  name-pl = itens ,

type = footnote ,
  gender = f ,
  Name-sg = Nota ,
  name-sg = nota ,
  Name-pl = Notas ,
  name-pl = notas ,

type = endnote ,
  gender = f ,
  Name-sg = Nota ,
  name-sg = nota ,
  Name-pl = Notas ,
  name-pl = notas ,

type = note ,
  gender = f ,
  Name-sg = Nota ,
  name-sg = nota ,
  Name-pl = Notas ,
  name-pl = notas ,

type = equation ,
  gender = f ,
  Name-sg = Equação ,
  name-sg = equação ,
  Name-pl = Equações ,
  name-pl = equações ,
  Name-sg-ab = Eq. ,
  name-sg-ab = eq. ,
  Name-pl-ab = Eqs. ,
  name-pl-ab = eqs. ,
  refbounds-first-sg = {,(,),} ,
  refbounds = {(,,,)} ,

type = theorem ,
  gender = m ,
  Name-sg = Teorema ,
  name-sg = teorema ,
  Name-pl = Teoremas ,
  name-pl = teoremas ,

type = lemma ,
  gender = m ,
  Name-sg = Lema ,
  name-sg = lema ,
  Name-pl = Lemas ,
  name-pl = lemas ,

type = corollary ,
  gender = m ,
  Name-sg = Corolário ,
  name-sg = corolário ,
  Name-pl = Corolários ,
  name-pl = corolários ,

type = proposition ,
  gender = f ,
  Name-sg = Proposição ,
  name-sg = proposição ,
  Name-pl = Proposições ,
  name-pl = proposições ,

type = definition ,
  gender = f ,
  Name-sg = Definição ,
  name-sg = definição ,
  Name-pl = Definições ,
  name-pl = definições ,

type = proof ,
  gender = f ,
  Name-sg = Demonstração ,
  name-sg = demonstração ,
  Name-pl = Demonstrações ,
  name-pl = demonstrações ,

type = result ,
  gender = m ,
  Name-sg = Resultado ,
  name-sg = resultado ,
  Name-pl = Resultados ,
  name-pl = resultados ,

type = remark ,
  gender = f ,
  Name-sg = Observação ,
  name-sg = observação ,
  Name-pl = Observações ,
  name-pl = observações ,

type = example ,
  gender = m ,
  Name-sg = Exemplo ,
  name-sg = exemplo ,
  Name-pl = Exemplos ,
  name-pl = exemplos ,

type = algorithm ,
  gender = m ,
  Name-sg = Algoritmo ,
  name-sg = algoritmo ,
  Name-pl = Algoritmos ,
  name-pl = algoritmos ,

type = listing ,
  gender = f ,
  Name-sg = Listagem ,
  name-sg = listagem ,
  Name-pl = Listagens ,
  name-pl = listagens ,

type = exercise ,
  gender = m ,
  Name-sg = Exercício ,
  name-sg = exercício ,
  Name-pl = Exercícios ,
  name-pl = exercícios ,

type = solution ,
  gender = f ,
  Name-sg = Solução ,
  name-sg = solução ,
  Name-pl = Soluções ,
  name-pl = soluções ,
%    \end{macrocode}
%
%    \begin{macrocode}
%</lang-portuguese>
%    \end{macrocode}
%
%
%
% \subsection{Spanish}
%
% Spanish language file has been initially provided by the author.
%
%    \begin{macrocode}
%<*package>
\zcDeclareLanguage [ gender = { f , m } ] { spanish }
%</package>
%    \end{macrocode}
%
%    \begin{macrocode}
%<*lang-spanish>
%    \end{macrocode}
%
%    \begin{macrocode}
namesep  = {\nobreakspace} ,
pairsep  = {~y\nobreakspace} ,
listsep  = {,~} ,
lastsep  = {~y\nobreakspace} ,
tpairsep = {~y\nobreakspace} ,
tlistsep = {,~} ,
tlastsep = {~y\nobreakspace} ,
notesep  = {~} ,
rangesep = {~a\nobreakspace} ,

type = book ,
  gender = m ,
  Name-sg =  Libro ,
  name-sg =  libro ,
  Name-pl =  Libros ,
  name-pl =  libros ,

type = part ,
  gender = f ,
  Name-sg = Parte ,
  name-sg = parte ,
  Name-pl = Partes ,
  name-pl = partes ,

type = chapter ,
  gender = m ,
  Name-sg = Capítulo ,
  name-sg = capítulo ,
  Name-pl = Capítulos ,
  name-pl = capítulos ,

type = section ,
  gender = f ,
  Name-sg = Sección ,
  name-sg = sección ,
  Name-pl = Secciones ,
  name-pl = secciones ,

type = paragraph ,
  gender = m ,
  Name-sg = Párrafo ,
  name-sg = párrafo ,
  Name-pl = Párrafos ,
  name-pl = párrafos ,

type = appendix ,
  gender = m ,
  Name-sg = Apéndice ,
  name-sg = apéndice ,
  Name-pl = Apéndices ,
  name-pl = apéndices ,

type = page ,
  gender = f ,
  Name-sg = Página ,
  name-sg = página ,
  Name-pl = Páginas ,
  name-pl = páginas ,
  rangesep = {\textendash} ,
  rangetopair = false ,

type = line ,
  gender = f ,
  Name-sg = Línea ,
  name-sg = línea ,
  Name-pl = Líneas ,
  name-pl = líneas ,

type = figure ,
  gender = f ,
  Name-sg = Figura ,
  name-sg = figura ,
  Name-pl = Figuras ,
  name-pl = figuras ,

type = table ,
  gender = m ,
  Name-sg = Cuadro ,
  name-sg = cuadro ,
  Name-pl = Cuadros ,
  name-pl = cuadros ,

type = item ,
  gender = m ,
  Name-sg = Punto ,
  name-sg = punto ,
  Name-pl = Puntos ,
  name-pl = puntos ,

type = footnote ,
  gender = f ,
  Name-sg = Nota ,
  name-sg = nota ,
  Name-pl = Notas ,
  name-pl = notas ,

type = endnote ,
  gender = f ,
  Name-sg = Nota ,
  name-sg = nota ,
  Name-pl = Notas ,
  name-pl = notas ,

type = note ,
  gender = f ,
  Name-sg = Nota ,
  name-sg = nota ,
  Name-pl = Notas ,
  name-pl = notas ,

type = equation ,
  gender = f ,
  Name-sg = Ecuación ,
  name-sg = ecuación ,
  Name-pl = Ecuaciones ,
  name-pl = ecuaciones ,
  refbounds-first-sg = {,(,),} ,
  refbounds = {(,,,)} ,

type = theorem ,
  gender = m ,
  Name-sg = Teorema ,
  name-sg = teorema ,
  Name-pl = Teoremas ,
  name-pl = teoremas ,

type = lemma ,
  gender = m ,
  Name-sg = Lema ,
  name-sg = lema ,
  Name-pl = Lemas ,
  name-pl = lemas ,

type = corollary ,
  gender = m ,
  Name-sg = Corolario ,
  name-sg = corolario ,
  Name-pl = Corolarios ,
  name-pl = corolarios ,

type = proposition ,
  gender = f ,
  Name-sg = Proposición ,
  name-sg = proposición ,
  Name-pl = Proposiciones ,
  name-pl = proposiciones ,

type = definition ,
  gender = f ,
  Name-sg = Definición ,
  name-sg = definición ,
  Name-pl = Definiciones ,
  name-pl = definiciones ,

type = proof ,
  gender = f ,
  Name-sg = Demostración ,
  name-sg = demostración ,
  Name-pl = Demostraciones ,
  name-pl = demostraciones ,

type = result ,
  gender = m ,
  Name-sg = Resultado ,
  name-sg = resultado ,
  Name-pl = Resultados ,
  name-pl = resultados ,

type = remark ,
  gender = f ,
  Name-sg = Observación ,
  name-sg = observación ,
  Name-pl = Observaciones ,
  name-pl = observaciones ,

type = example ,
  gender = m ,
  Name-sg = Ejemplo ,
  name-sg = ejemplo ,
  Name-pl = Ejemplos ,
  name-pl = ejemplos ,

type = algorithm ,
  gender = m ,
  Name-sg = Algoritmo ,
  name-sg = algoritmo ,
  Name-pl = Algoritmos ,
  name-pl = algoritmos ,

type = listing ,
  gender = m ,
  Name-sg = Listado ,
  name-sg = listado ,
  Name-pl = Listados ,
  name-pl = listados ,

type = exercise ,
  gender = m ,
  Name-sg = Ejercicio ,
  name-sg = ejercicio ,
  Name-pl = Ejercicios ,
  name-pl = ejercicios ,

type = solution ,
  gender = f ,
  Name-sg = Solución ,
  name-sg = solución ,
  Name-pl = Soluciones ,
  name-pl = soluciones ,
%    \end{macrocode}
%
%    \begin{macrocode}
%</lang-spanish>
%    \end{macrocode}
%
%
%
% \subsection{Dutch}
%
% Dutch language file initially contributed by \contributor{\username{niluxv}}
% (\githubPR{5}).  All genders were checked against the ``Dikke Van Dale''.
% Many words have multiple genders.
%
%    \begin{macrocode}
%<*package>
\zcDeclareLanguage [ gender = { f , m , n } ] { dutch }
%</package>
%    \end{macrocode}
%
%    \begin{macrocode}
%<*lang-dutch>
%    \end{macrocode}
%
%    \begin{macrocode}
namesep   = {\nobreakspace} ,
pairsep   = {~en\nobreakspace} ,
listsep   = {,~} ,
lastsep   = {~en\nobreakspace} ,
tpairsep  = {~en\nobreakspace} ,
tlistsep  = {,~} ,
tlastsep  = {,~en\nobreakspace} ,
notesep   = {~} ,
rangesep  = {~t/m\nobreakspace} ,

type = book ,
  gender = n ,
  Name-sg = Boek ,
  name-sg = boek ,
  Name-pl = Boeken ,
  name-pl = boeken ,

type = part ,
  gender = n ,
  Name-sg = Deel ,
  name-sg = deel ,
  Name-pl = Delen ,
  name-pl = delen ,

type = chapter ,
  gender = n ,
  Name-sg = Hoofdstuk ,
  name-sg = hoofdstuk ,
  Name-pl = Hoofdstukken ,
  name-pl = hoofdstukken ,

type = section ,
  gender = m ,
  Name-sg = Paragraaf ,
  name-sg = paragraaf ,
  Name-pl = Paragrafen ,
  name-pl = paragrafen ,

type = paragraph ,
  gender = f ,
  Name-sg = Alinea ,
  name-sg = alinea ,
  Name-pl = Alinea's ,
  name-pl = alinea's ,

%    \end{macrocode}
% 2022-12-27, \contributor{\username{niluxv}}: ``bijlage'' is chosen over
% ``appendix'' (plural ``appendices'', gender: m, n) for consistency with
% babel/polyglossia. ``bijlages'' is also a valid plural; ``bijlagen'' is
% chosen for consistency with babel/polyglossia.
%    \begin{macrocode}
type = appendix ,
  gender = { f, m } ,
  Name-sg = Bĳlage ,
  name-sg = bĳlage ,
  Name-pl = Bĳlagen ,
  name-pl = bĳlagen ,

type = page ,
  gender = { f , m } ,
  Name-sg = Pagina ,
  name-sg = pagina ,
  Name-pl = Pagina's ,
  name-pl = pagina's ,
  rangesep = {\textendash} ,
  rangetopair = false ,

type = line ,
  gender = m ,
  Name-sg = Regel ,
  name-sg = regel ,
  Name-pl = Regels ,
  name-pl = regels ,

type = figure ,
  gender = { n , f , m } ,
  Name-sg = Figuur ,
  name-sg = figuur ,
  Name-pl = Figuren ,
  name-pl = figuren ,

type = table ,
  gender = { f , m } ,
  Name-sg = Tabel ,
  name-sg = tabel ,
  Name-pl = Tabellen ,
  name-pl = tabellen ,

type = item ,
  gender = n ,
  Name-sg = Punt ,
  name-sg = punt ,
  Name-pl = Punten ,
  name-pl = punten ,

type = footnote ,
  gender = { f , m } ,
  Name-sg = Voetnoot ,
  name-sg = voetnoot ,
  Name-pl = Voetnoten ,
  name-pl = voetnoten ,

type = endnote ,
  gender = { f , m } ,
  Name-sg = Eindnoot ,
  name-sg = eindnoot ,
  Name-pl = Eindnoten ,
  name-pl = eindnoten ,

type = note ,
  gender = f ,
  Name-sg = Opmerking ,
  name-sg = opmerking ,
  Name-pl = Opmerkingen ,
  name-pl = opmerkingen ,

type = equation ,
  gender = f ,
  Name-sg = Vergelĳking ,
  name-sg = vergelĳking ,
  Name-pl = Vergelĳkingen ,
  name-pl = vergelĳkingen ,
  Name-sg-ab = Vgl. ,
  name-sg-ab = vgl. ,
  Name-pl-ab = Vgl.'s ,
  name-pl-ab = vgl.'s ,
  refbounds-first-sg = {,(,),} ,
  refbounds = {(,,,)} ,

type = theorem ,
  gender = f ,
  Name-sg = Stelling ,
  name-sg = stelling ,
  Name-pl = Stellingen ,
  name-pl = stellingen ,

%    \end{macrocode}
% 2022-01-09, \contributor{\username{niluxv}}: An alternative plural is
% ``lemmata''.  That is also a correct English plural for lemma, but the
% English language file chooses ``lemmas''.  For consistency we therefore
% choose ``lemma's''.
%    \begin{macrocode}
type = lemma ,
  gender = n ,
  Name-sg = Lemma ,
  name-sg = lemma ,
  Name-pl = Lemma's ,
  name-pl = lemma's ,

type = corollary ,
  gender = n ,
  Name-sg = Gevolg ,
  name-sg = gevolg ,
  Name-pl = Gevolgen ,
  name-pl = gevolgen ,

type = proposition ,
  gender = f ,
  Name-sg = Propositie ,
  name-sg = propositie ,
  Name-pl = Proposities ,
  name-pl = proposities ,

type = definition ,
  gender = f ,
  Name-sg = Definitie ,
  name-sg = definitie ,
  Name-pl = Definities ,
  name-pl = definities ,

type = proof ,
  gender = n ,
  Name-sg = Bewĳs ,
  name-sg = bewĳs ,
  Name-pl = Bewĳzen ,
  name-pl = bewĳzen ,

type = result ,
  gender = n ,
  Name-sg = Resultaat ,
  name-sg = resultaat ,
  Name-pl = Resultaten ,
  name-pl = resultaten ,

type = remark ,
  gender = f ,
  Name-sg = Opmerking ,
  name-sg = opmerking ,
  Name-pl = Opmerkingen ,
  name-pl = opmerkingen ,

type = example ,
  gender = n ,
  Name-sg = Voorbeeld ,
  name-sg = voorbeeld ,
  Name-pl = Voorbeelden ,
  name-pl = voorbeelden ,

%    \end{macrocode}
% 2022-12-27, \contributor{\username{niluxv}}: ``algoritmes'' is also a valid
% plural. ``algoritmen'' is chosen to be consistent with using ``bijlagen''
% (and not ``bijlages'') as the plural of ``bijlage''.
%    \begin{macrocode}
type = algorithm ,
  gender = { n , f , m } ,
  Name-sg = Algoritme ,
  name-sg = algoritme ,
  Name-pl = Algoritmen ,
  name-pl = algoritmen ,

%    \end{macrocode}
% 2022-01-09, \contributor{\username{niluxv}}: EN-NL Van Dale translates
% listing as (3) ``uitdraai van computerprogramma'', ``listing''.
%    \begin{macrocode}
type = listing ,
  gender = m ,
  Name-sg = Listing ,
  name-sg = listing ,
  Name-pl = Listings ,
  name-pl = listings ,

type = exercise ,
  gender = { f , m } ,
  Name-sg = Opgave ,
  name-sg = opgave ,
  Name-pl = Opgaven ,
  name-pl = opgaven ,

type = solution ,
  gender = f ,
  Name-sg = Oplossing ,
  name-sg = oplossing ,
  Name-pl = Oplossingen ,
  name-pl = oplossingen ,
%    \end{macrocode}
%
%    \begin{macrocode}
%</lang-dutch>
%    \end{macrocode}
%
%
% \subsection{Italian}
%
% Italian language file initially contributed by \contributor{Matteo
% Ferrigato} (\githubissue{11}), with the help of participants of the Gruppo
% Utilizzatori Italiani di \TeX{} (GuIT) forum (at
% \url{https://www.guitex.org/home/it/forum/5-tex-e-latex/121856-closed-zref-clever-e-localizzazione-in-italiano}).
%
%    \begin{macrocode}
%<*package>
\zcDeclareLanguage [ gender = { f , m } ] { italian }
%</package>
%    \end{macrocode}
%
%    \begin{macrocode}
%<*lang-italian>
%    \end{macrocode}
%
%    \begin{macrocode}
namesep   = {\nobreakspace} ,
pairsep   = {~e\nobreakspace} ,
listsep   = {,~} ,
lastsep   = {~e\nobreakspace} ,
tpairsep  = {~e\nobreakspace} ,
tlistsep  = {,~} ,
tlastsep  = {,~e\nobreakspace} ,
notesep   = {~} ,
rangesep  = {~a\nobreakspace} ,
+refbounds-rb = {da\nobreakspace,,,} ,

type = book ,
  gender = m ,
  Name-sg = Libro ,
  name-sg = libro ,
  Name-pl = Libri ,
  name-pl = libri ,

type = part ,
  gender = f ,
  Name-sg = Parte ,
  name-sg = parte ,
  Name-pl = Parti ,
  name-pl = parti ,

type = chapter ,
  gender = m ,
  Name-sg = Capitolo ,
  name-sg = capitolo ,
  Name-pl = Capitoli ,
  name-pl = capitoli ,

type = section ,
  gender = m ,
  Name-sg = Paragrafo ,
  name-sg = paragrafo ,
  Name-pl = Paragrafi ,
  name-pl = paragrafi ,

type = paragraph ,
  gender = m ,
  Name-sg = Capoverso ,
  name-sg = capoverso ,
  Name-pl = Capoversi ,
  name-pl = capoversi ,

type = appendix ,
  gender = f ,
  Name-sg = Appendice ,
  name-sg = appendice ,
  Name-pl = Appendici ,
  name-pl = appendici ,

type = page ,
  gender = f ,
  Name-sg = Pagina ,
  name-sg = pagina ,
  Name-pl = Pagine ,
  name-pl = pagine ,
  Name-sg-ab = Pag. ,
  name-sg-ab = pag. ,
  Name-pl-ab = Pag. ,
  name-pl-ab = pag. ,
  rangesep = {\textendash} ,
  rangetopair = false ,
  +refbounds-rb = {,,,} ,

type = line ,
  gender = f ,
  Name-sg = Riga ,
  name-sg = riga ,
  Name-pl = Righe ,
  name-pl = righe ,

type = figure ,
  gender = f ,
  Name-sg = Figura ,
  name-sg = figura ,
  Name-pl = Figure ,
  name-pl = figure ,
  Name-sg-ab = Fig. ,
  name-sg-ab = fig. ,
  Name-pl-ab = Fig. ,
  name-pl-ab = fig. ,

type = table ,
  gender = f ,
  Name-sg = Tabella ,
  name-sg = tabella ,
  Name-pl = Tabelle ,
  name-pl = tabelle ,
  Name-sg-ab = Tab. ,
  name-sg-ab = tab. ,
  Name-pl-ab = Tab. ,
  name-pl-ab = tab. ,

type = item ,
  gender = m ,
  Name-sg = Punto ,
  name-sg = punto ,
  Name-pl = Punti ,
  name-pl = punti ,

type = footnote ,
  gender = f ,
  Name-sg = Nota ,
  name-sg = nota ,
  Name-pl = Note ,
  name-pl = note ,

type = endnote ,
  gender = f ,
  Name-sg = Nota ,
  name-sg = nota ,
  Name-pl = Note ,
  name-pl = note ,

type = note ,
  gender = f ,
  Name-sg = Nota ,
  name-sg = nota ,
  Name-pl = Note ,
  name-pl = note ,

type = equation ,
  gender = f ,
  Name-sg = Equazione ,
  name-sg = equazione ,
  Name-pl = Equazioni ,
  name-pl = equazioni ,
  Name-sg-ab = Eq. ,
  name-sg-ab = eq. ,
  Name-pl-ab = Eq. ,
  name-pl-ab = eq. ,
  +refbounds-rb = {da\nobreakspace(,,,)} ,
  refbounds-first-sg = {,(,),} ,
  refbounds = {(,,,)} ,

type = theorem ,
  gender = m ,
  Name-sg = Teorema ,
  name-sg = teorema ,
  Name-pl = Teoremi ,
  name-pl = teoremi ,

type = lemma ,
  gender = m ,
  Name-sg = Lemma ,
  name-sg = lemma ,
  Name-pl = Lemmi ,
  name-pl = lemmi ,

type = corollary ,
  gender = m ,
  Name-sg = Corollario ,
  name-sg = corollario ,
  Name-pl = Corollari ,
  name-pl = corollari ,

type = proposition ,
  gender = f ,
  Name-sg = Proposizione ,
  name-sg = proposizione ,
  Name-pl = Proposizioni ,
  name-pl = proposizioni ,

type = definition ,
  gender = f ,
  Name-sg = Definizione ,
  name-sg = definizione ,
  Name-pl = Definizioni ,
  name-pl = definizioni ,

type = proof ,
  gender = f ,
  Name-sg = Dimostrazione ,
  name-sg = dimostrazione ,
  Name-pl = Dimostrazioni ,
  name-pl = dimostrazioni ,

type = result ,
  gender = m ,
  Name-sg = Risultato ,
  name-sg = risultato ,
  Name-pl = Risultati ,
  name-pl = risultati ,

type = remark ,
  gender = f ,
  Name-sg = Osservazione ,
  name-sg = osservazione ,
  Name-pl = Osservazioni ,
  name-pl = osservazioni ,

type = example ,
  gender = m ,
  Name-sg = Esempio ,
  name-sg = esempio ,
  Name-pl = Esempi ,
  name-pl = esempi ,

type = algorithm ,
  gender = m ,
  Name-sg = Algoritmo ,
  name-sg = algoritmo ,
  Name-pl = Algoritmi ,
  name-pl = algoritmi ,

type = listing ,
  gender = m ,
  Name-sg = Listato ,
  name-sg = listato ,
  Name-pl = Listati ,
  name-pl = listati ,

type = exercise ,
  gender = m ,
  Name-sg = Esercizio ,
  name-sg = esercizio ,
  Name-pl = Esercizi ,
  name-pl = esercizi ,

type = solution ,
  gender = f ,
  Name-sg = Soluzione ,
  name-sg = soluzione ,
  Name-pl = Soluzioni ,
  name-pl = soluzioni ,
%    \end{macrocode}
%
%    \begin{macrocode}
%</lang-italian>
%    \end{macrocode}
%
%
% \PrintIndex
%
%
