texlive[61935] Master/texmf-dist: zref-clever (7feb22)

commits+karl at tug.org commits+karl at tug.org
Mon Feb 7 22:40:53 CET 2022


Revision: 61935
          http://tug.org/svn/texlive?view=revision&revision=61935
Author:   karl
Date:     2022-02-07 22:40:53 +0100 (Mon, 07 Feb 2022)
Log Message:
-----------
zref-clever (7feb22)

Modified Paths:
--------------
    trunk/Master/texmf-dist/doc/latex/zref-clever/CHANGELOG.md
    trunk/Master/texmf-dist/doc/latex/zref-clever/MANIFEST.md
    trunk/Master/texmf-dist/doc/latex/zref-clever/zref-clever-code.pdf
    trunk/Master/texmf-dist/doc/latex/zref-clever/zref-clever.pdf
    trunk/Master/texmf-dist/doc/latex/zref-clever/zref-clever.tex
    trunk/Master/texmf-dist/source/latex/zref-clever/zref-clever.dtx
    trunk/Master/texmf-dist/tex/latex/zref-clever/zref-clever-dutch.lang
    trunk/Master/texmf-dist/tex/latex/zref-clever/zref-clever-english.lang
    trunk/Master/texmf-dist/tex/latex/zref-clever/zref-clever-french.lang
    trunk/Master/texmf-dist/tex/latex/zref-clever/zref-clever-german.lang
    trunk/Master/texmf-dist/tex/latex/zref-clever/zref-clever-portuguese.lang
    trunk/Master/texmf-dist/tex/latex/zref-clever/zref-clever-spanish.lang
    trunk/Master/texmf-dist/tex/latex/zref-clever/zref-clever.sty

Modified: trunk/Master/texmf-dist/doc/latex/zref-clever/CHANGELOG.md
===================================================================
--- trunk/Master/texmf-dist/doc/latex/zref-clever/CHANGELOG.md	2022-02-07 21:40:38 UTC (rev 61934)
+++ trunk/Master/texmf-dist/doc/latex/zref-clever/CHANGELOG.md	2022-02-07 21:40:53 UTC (rev 61935)
@@ -1,7 +1,17 @@
 # Changelog
 
-## [Unreleased](https://github.com/gusbrs/zref-clever/compare/v0.2.0-alpha...HEAD)
+## [Unreleased](https://github.com/gusbrs/zref-clever/compare/v0.2.1-alpha...HEAD)
 
+## [v0.2.1-alpha](https://github.com/gusbrs/zref-clever/compare/v0.2.0-alpha...v0.2.1-alpha) (2022-02-07)
+
+### Added
+- Option `endrange` for typesetting terse ranges.
+- Option `rangetopair` to control behavior of `range` option when a pair
+  results from building a range.
+- Option `vario` to load package `zref-vario`.
+- Reference property `subeq` for the `amsmath` and `breqn` compatibility
+  modules.
+
 ## [v0.2.0-alpha](https://github.com/gusbrs/zref-clever/compare/v0.1.2-alpha...v0.2.0-alpha) (2022-01-28)
 
 ### Added

Modified: trunk/Master/texmf-dist/doc/latex/zref-clever/MANIFEST.md
===================================================================
--- trunk/Master/texmf-dist/doc/latex/zref-clever/MANIFEST.md	2022-02-07 21:40:38 UTC (rev 61934)
+++ trunk/Master/texmf-dist/doc/latex/zref-clever/MANIFEST.md	2022-02-07 21:40:53 UTC (rev 61935)
@@ -66,8 +66,10 @@
 
 * zc-LanguageSetup01.lvt 
 * zc-LanguageSetup02.lvt 
+* zc-LanguageSetup03.lvt 
 * zc-RefTypeSetup01.lvt 
 * zc-RefTypeSetup02.lvt 
+* zc-RefTypeSetup03.lvt 
 * zc-class-article01.lvt 
 * zc-class-book01.lvt 
 * zc-class-memoir01.lvt 
@@ -98,6 +100,7 @@
 * zc-howto-newtheorem02.lvt 
 * zc-howto-newtheorem03.lvt 
 * zc-howto-newtheorem04.lvt 
+* zc-howto-zref-vario01.lvt 
 * zc-howto-zref-xr01.lvt 
 * zc-label-options01.lvt 
 * zc-langfile-dutch01.lvt 
@@ -127,6 +130,7 @@
 * zc-opt-ref01.lvt 
 * zc-opt-ref02.lvt 
 * zc-opt-titleref01.lvt 
+* zc-opt-vario01.lvt 
 * zc-precedence-rules01.lvt 
 * zc-sort01.lvt 
 * zc-typeset01.lvt 
@@ -133,6 +137,7 @@
 * zc-workaround-breqn01.lvt 
 * zc-zcref-options01.lvt 
 * zc-zcref-options02.lvt 
+* zc-zcref-options03.lvt 
 * zc-LanguageSetup01.luatex.tlg 
 * zc-LanguageSetup01.luatexdev.tlg 
 * zc-LanguageSetup01.tlg 
@@ -143,6 +148,11 @@
 * zc-LanguageSetup02.tlg 
 * zc-LanguageSetup02.xetex.tlg 
 * zc-LanguageSetup02.xetexdev.tlg 
+* zc-LanguageSetup03.luatex.tlg 
+* zc-LanguageSetup03.luatexdev.tlg 
+* zc-LanguageSetup03.tlg 
+* zc-LanguageSetup03.xetex.tlg 
+* zc-LanguageSetup03.xetexdev.tlg 
 * zc-RefTypeSetup01.luatex.tlg 
 * zc-RefTypeSetup01.luatexdev.tlg 
 * zc-RefTypeSetup01.tlg 
@@ -153,6 +163,11 @@
 * zc-RefTypeSetup02.tlg 
 * zc-RefTypeSetup02.xetex.tlg 
 * zc-RefTypeSetup02.xetexdev.tlg 
+* zc-RefTypeSetup03.luatex.tlg 
+* zc-RefTypeSetup03.luatexdev.tlg 
+* zc-RefTypeSetup03.tlg 
+* zc-RefTypeSetup03.xetex.tlg 
+* zc-RefTypeSetup03.xetexdev.tlg 
 * zc-class-article01.luatex.tlg 
 * zc-class-article01.luatexdev.tlg 
 * zc-class-article01.tlg 
@@ -303,6 +318,11 @@
 * zc-howto-newtheorem04.tlg 
 * zc-howto-newtheorem04.xetex.tlg 
 * zc-howto-newtheorem04.xetexdev.tlg 
+* zc-howto-zref-vario01.luatex.tlg 
+* zc-howto-zref-vario01.luatexdev.tlg 
+* zc-howto-zref-vario01.tlg 
+* zc-howto-zref-vario01.xetex.tlg 
+* zc-howto-zref-vario01.xetexdev.tlg 
 * zc-howto-zref-xr01.luatex.tlg 
 * zc-howto-zref-xr01.luatexdev.tlg 
 * zc-howto-zref-xr01.tlg 
@@ -408,6 +428,11 @@
 * zc-opt-titleref01.tlg 
 * zc-opt-titleref01.xetex.tlg 
 * zc-opt-titleref01.xetexdev.tlg 
+* zc-opt-vario01.luatex.tlg 
+* zc-opt-vario01.luatexdev.tlg 
+* zc-opt-vario01.tlg 
+* zc-opt-vario01.xetex.tlg 
+* zc-opt-vario01.xetexdev.tlg 
 * zc-precedence-rules01.luatex.tlg 
 * zc-precedence-rules01.luatexdev.tlg 
 * zc-precedence-rules01.tlg 
@@ -434,6 +459,11 @@
 * zc-zcref-options02.tlg 
 * zc-zcref-options02.xetex.tlg 
 * zc-zcref-options02.xetexdev.tlg 
+* zc-zcref-options03.luatex.tlg 
+* zc-zcref-options03.luatexdev.tlg 
+* zc-zcref-options03.tlg 
+* zc-zcref-options03.xetex.tlg 
+* zc-zcref-options03.xetexdev.tlg 
 
 
 ## TDS manifest

Modified: trunk/Master/texmf-dist/doc/latex/zref-clever/zref-clever-code.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/doc/latex/zref-clever/zref-clever.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/doc/latex/zref-clever/zref-clever.tex
===================================================================
--- trunk/Master/texmf-dist/doc/latex/zref-clever/zref-clever.tex	2022-02-07 21:40:38 UTC (rev 61934)
+++ trunk/Master/texmf-dist/doc/latex/zref-clever/zref-clever.tex	2022-02-07 21:40:53 UTC (rev 61935)
@@ -105,6 +105,11 @@
     zcLanguageSetup,
     zcRefTypeSetup,
     zcheck,
+    zvref,
+    zvpageref,
+    zvrefrange,
+    zvpagerefrange,
+    zfullref,
   }
 }
 \lstset{
@@ -266,14 +271,14 @@
 local options.
 
 Considering that \pkg{zref} itself offers the \pkg{zref-titleref} module, and
-that the integration with \pkg{zref-check} allows \pkg{zref-clever} to make
-context sensitive references, a significant part of the most prominent
-automation features available to the standard referencing system is thus
-brought to \pkg{zref}, working under a single consistent underlying
-infrastructure and user interface.  There are some limitations, of course (see
-\zcref{sec:limitations}), and it may be your cup of tea or not.  Still, all in
-all, hopefully \pkg{zref-clever} can make \pkg{zref} more accessible, and
-interesting, to the average user.
+that \pkg{zref-vario} offers integration of \pkg{zref-clever} with
+\pkg{varioref}, a significant part of the most prominent automation features
+available to the standard referencing system is thus brought to \pkg{zref},
+working under a single consistent underlying infrastructure and user
+interface.  Alas, there are some limitations (see \zcref{sec:limitations}),
+and it may be your cup of tea or not.  Still, all in all, hopefully
+\pkg{zref-clever} can make \pkg{zref} more accessible to the average user, and
+more interesting to users in general.
 
 
 \section{Warning}
@@ -501,7 +506,43 @@
 for \texttt{comp=false}.  Of course, for better compression results the
 \opt{sort} is recommended, but the two options are technically independent.
 
+\DescribeOption{endrange} %
+The \opt{endrange} option provides additional control over how the end
+reference of a range is typeset, so as to achieve terse ranges.  The option
+can operate in two technically different ways.  It may receive one of a number
+of predefined values, which can process the end reference of the range,
+comparing it with the beginning reference, to achieve a given end
+result.\footnote{For the \TeX{}nically inclined: those values that perform
+  some processing -- namely \texttt{stripprefix}, \texttt{pagecomp}, and
+  \texttt{pagecomp2} -- fully expand the references (\texttt{x}-type
+  expansion) before comparing them, since it makes sense to perform this task
+  as close as possible to the printed representation of the references.  I
+  don't expect this to be a problem in normal use cases, but it does represent
+  a limitation on what the references can contain.  In case some control over
+  this is needed, check the \texttt{zref-clever/endrange-setup} hook in the
+  code documentation.}  Or, it can specify a label property to be used
+directly, without any processing.  The available predefined values are:
+\texttt{ref}, \texttt{stripprefix}, \texttt{pagecomp}, and \texttt{pagecomp2}.
+\texttt{ref} corresponds to the default behavior, and instructs \cs{zcref} to
+use whatever property was set at the \opt{ref} option for the end of range
+reference.  \texttt{stripprefix} strips the common part at the start of each
+reference from the end one.  \texttt{pagecomp} is the equivalent of
+\texttt{stripprefix} for page numbers, it does the same thing, but only if the
+references are comprised exclusively of Arabic numerals.  \texttt{pagecomp2}
+is a variant of \texttt{pagecomp} that leaves at least two digits at the end
+reference (except for a leading zero).  If values other than the predefined
+ones are given to \opt{endrange} they are considered as label properties, as
+long as they are declared.  This property is used to typeset the end of range
+reference if the label contains it, and if both references would be
+``compressible'' according to the \opt{comp} option, otherwise the property
+specified by the \opt{ref} option is used.  This is useful for things like
+sub-elements for which we can build a proper abbreviated sub-reference and
+populate the label with it (some compatibility modules already provide a
+number of such properties, but other ones can be built with \pkg{zref}, as
+needed).
+
 \DescribeOption{range} %
+\DescribeOption{rangetopair} %
 By default (that is, when the \opt{range} option is not given), \cs{zcref}
 typesets a complete list of references according to the \meta{labels} it
 received as argument, and only compresses some of them into ranges if the
@@ -510,12 +551,14 @@
 differently.  Sorting is implied by this option (the \opt{sort} option is
 disregarded) and, for each reference type group in \meta{labels}, \cs{zcref}
 builds a range from the first to the last reference in it, even if references
-in between do not occur in immediate sequence.  \cs{zcref} is smart enough,
-though, to recognize when the first and last references of a type do happen to
-be contiguous, in which case it typesets a ``pair'', instead of a ``range''.
-It is a boolean option, and the package's default is \texttt{range=false}.
-The option given without a value is equivalent to \texttt{range=true} (in the
-\pkg{l3keys}' jargon, the \emph{option}'s default is \texttt{true}).
+in between do not occur in immediate sequence.  It is a boolean option, and
+the package's default is \texttt{range=false}.  The option given without a
+value is equivalent to \texttt{range=true} (in the \pkg{l3keys}' jargon, the
+\emph{option}'s default is \texttt{true}).  \cs{zcref} is smart enough to
+recognize when the first and last references of a type do happen to be
+contiguous, in which case it typesets a ``pair'', instead of a ``range''.  But
+this behavior can be disabled by setting the \opt{rangetopair} option to
+\texttt{false}.
 
 \DescribeOption{cap} %
 \DescribeOption{nocap} %
@@ -666,8 +709,14 @@
 you want to set it globally, use \cs{zcsetup} instead.
 
 \DescribeOption{titleref} %
+\DescribeOption{vario} %
 The \opt{titleref} option receives no value and, when given, loads
-\pkg{zref}'s \pkg{zref-titleref} module.  This is a preamble only option.
+\pkg{zref}'s \pkg{zref-titleref} module.  Similarly, the \opt{vario} option
+loads the \pkg{zref-vario} package.  These are a preamble only options.  Note
+that \pkg{zref-vario} loads \pkg{varioref}, which has known load order
+interaction with other packages, prominently with \pkg{hyperref}.  Hence,
+depending on your document, you may wish to load \pkg{zref-vario} separately,
+instead of through the option.
 
 \DescribeOption{note} %
 The \opt{note} option receives as value some text to be typeset at the end of
@@ -847,7 +896,7 @@
 
 One more observation about ``reference types'' is due here.  A \emph{type} is
 not really ``defined'' in the sense a variable or a function is.  It is more
-of a ``string'' which \pkg{zref-clever} uses to look for a whole set of
+of a ``name'' which \pkg{zref-clever} uses to look for a whole set of
 type-specific reference format options (see \zcref{sec:reference-format}).
 Each of these options individually may be ``set'' or not, ``defined'' or not.
 And, depending on the setup and the relevant precedence rules for this, some
@@ -908,42 +957,44 @@
   \centering
   \begin{tabular}{l>{\ttfamily}lcccc}
     \toprule
-                     &            & General   & Type      & \multicolumn{2}{c}{Language} \\
-                                                            \cmidrule(lr){5-6}
-                     &            &           &           & Type      & Default   \\
-                     &            & (i)       & (ii)      & (iii)     & (iv)      \\
+                     &             & General   & Type      & \multicolumn{2}{c}{Language} \\
+                                                             \cmidrule(lr){5-6}
+                     &             &           &           & Type      & Default   \\
+                     &             & (i)       & (ii)      & (iii)     & (iv)      \\
 
     \midrule
-    Typesetting      & tpairsep   & $\bullet$ &           &           & $\bullet$ \\
-    (necessarily not & tlistsep   & $\bullet$ &           &           & $\bullet$ \\
-    type-specific)   & tlastsep   & $\bullet$ &           &           & $\bullet$ \\
-                     & notesep    & $\bullet$ &           &           & $\bullet$ \\
+    Typesetting      & tpairsep    & $\bullet$ &           &           & $\bullet$ \\
+    (necessarily not & tlistsep    & $\bullet$ &           &           & $\bullet$ \\
+    type-specific)   & tlastsep    & $\bullet$ &           &           & $\bullet$ \\
+                     & notesep     & $\bullet$ &           &           & $\bullet$ \\
 
     \addlinespace
-    Typesetting      & namesep    & $\bullet$ & $\bullet$ & $\bullet$ & $\bullet$ \\
-    (possibly        & pairsep    & $\bullet$ & $\bullet$ & $\bullet$ & $\bullet$ \\
-    type-specific)   & listsep    & $\bullet$ & $\bullet$ & $\bullet$ & $\bullet$ \\
-                     & lastsep    & $\bullet$ & $\bullet$ & $\bullet$ & $\bullet$ \\
-                     & rangesep   & $\bullet$ & $\bullet$ & $\bullet$ & $\bullet$ \\
-                     & refbounds  & $\bullet$ & $\bullet$ & $\bullet$ & $\bullet$ \\
+    Typesetting      & namesep     & $\bullet$ & $\bullet$ & $\bullet$ & $\bullet$ \\
+    (possibly        & pairsep     & $\bullet$ & $\bullet$ & $\bullet$ & $\bullet$ \\
+    type-specific)   & listsep     & $\bullet$ & $\bullet$ & $\bullet$ & $\bullet$ \\
+                     & lastsep     & $\bullet$ & $\bullet$ & $\bullet$ & $\bullet$ \\
+                     & rangesep    & $\bullet$ & $\bullet$ & $\bullet$ & $\bullet$ \\
+                     & refbounds   & $\bullet$ & $\bullet$ & $\bullet$ & $\bullet$ \\
 
     \addlinespace
-    Typesetting      & Name-sg    &           & $\bullet$ & $\bullet$ &           \\
-    (necessarily     & name-sg    &           & $\bullet$ & $\bullet$ &           \\
-    type-specific)   & Name-pl    &           & $\bullet$ & $\bullet$ &           \\
-                     & name-pl    &           & $\bullet$ & $\bullet$ &           \\
-                     & Name-sg-ab &           & $\bullet$ & $\bullet$ &           \\
-                     & name-sg-ab &           & $\bullet$ & $\bullet$ &           \\
-                     & Name-pl-ab &           & $\bullet$ & $\bullet$ &           \\
-                     & name-pl-ab &           & $\bullet$ & $\bullet$ &           \\
+    Typesetting      & Name-sg     &           & $\bullet$ & $\bullet$ &           \\
+    (necessarily     & name-sg     &           & $\bullet$ & $\bullet$ &           \\
+    type-specific)   & Name-pl     &           & $\bullet$ & $\bullet$ &           \\
+                     & name-pl     &           & $\bullet$ & $\bullet$ &           \\
+                     & Name-sg-ab  &           & $\bullet$ & $\bullet$ &           \\
+                     & name-sg-ab  &           & $\bullet$ & $\bullet$ &           \\
+                     & Name-pl-ab  &           & $\bullet$ & $\bullet$ &           \\
+                     & name-pl-ab  &           & $\bullet$ & $\bullet$ &           \\
 
     \addlinespace
-    Font             & namefont   & $\bullet$ & $\bullet$ & $\bullet$ & $\bullet$ \\
-                     & reffont    & $\bullet$ & $\bullet$ & $\bullet$ & $\bullet$ \\
+    Font             & namefont    & $\bullet$ & $\bullet$ & $\bullet$ & $\bullet$ \\
+                     & reffont     & $\bullet$ & $\bullet$ & $\bullet$ & $\bullet$ \\
 
     \addlinespace
-    Other            & cap        & $\bullet$ & $\bullet$ & $\bullet$ & $\bullet$ \\
-                     & abbrev     & $\bullet$ & $\bullet$ & $\bullet$ & $\bullet$ \\
+    Other            & cap         & $\bullet$ & $\bullet$ & $\bullet$ & $\bullet$ \\
+                     & abbrev      & $\bullet$ & $\bullet$ & $\bullet$ & $\bullet$ \\
+                     & endrange    & $\bullet$ & $\bullet$ & $\bullet$ & $\bullet$ \\
+                     & rangetopair & $\bullet$ & $\bullet$ & $\bullet$ & $\bullet$ \\
     \bottomrule
   \end{tabular}
   \caption{Reference format options and their scopes}
@@ -1490,73 +1541,42 @@
 \pkg{zref-clever}.
 
 
-\subsection{Context sensitive page references}
+\subsection{Extended page references (\pkg{varioref})}
 
 \zctask{Make cross-references to pages which are sensitive to the relative
-  position between the reference and the label being referred to.}
+  position between the reference and the label being referred to using
+  \pkg{varioref}.}
 
-It is frequently useful to make a reference to both a document element and its
-page.  For example:
-\begin{zcexample}
-\zcref{fig:1} on \zcpageref{fig:1}
-\end{zcexample}
+\pkg{zref-vario}, which can be enabled with package option \opt{vario}, offers
+a layer of compatibility with \pkg{varioref} and provides
+\texttt{\textbackslash{}z}\dots{} counterparts for the latter's main reference
+commands.
 
-However, while this works well when the object and the reference are somewhat
-distant, we may want to adjust this to something different when the label and
-the reference are close to each other.  If both are on the same page, we may
-want to drop the \cs{zcpageref}, or replace it with ``above'' or ``below''.
-And, if both are on contiguous pages, then something like ``on the following
-page'' or ``on the preceding page'' may be preferred.  Since we don't usually
-know the relative position of the reference to the label, particularly for
-floats, making these kinds of adjustments manually is an uphill battle, and
-error prone, in the absence of some support for it.
+\begin{zchowto}[caption={\pkg{zref-vario}}]
+\documentclass{article}
+\usepackage[vario]{zref-clever}
+\begin{document}
+\section{Section 1}
+\zlabel{sec:section-1}
+\begin{figure}
+  A figure.
+  \caption{Figure 1}
+  \zlabel{fig:figure-1}
+\end{figure}
+\begin{figure}
+  Another figure.
+  \caption{Figure 2}
+  \zlabel{fig:figure-2}
+\end{figure}
+\zvref[S]{sec:section-1}
+\zvpageref{fig:figure-1}
+\zvrefrange{fig:figure-1}{fig:figure-2}
+\zvpagerefrange{fig:figure-1}{fig:figure-2}
+\zfullref{fig:figure-1}
+\end{document}
+\end{zchowto}
 
-\pkg{zref-clever}'s integration with \pkg{zref-check} offers some assistance
-in making these kinds of references, by providing checks for the relation
-between the reference and the label.  For example, you may use:
-\begin{zcexample}
-\zcref[check=nextpage,note={on the following page}]{fig:1}
-\end{zcexample}
-and if \texttt{fig:1} is not on the next page relative to the reference, a
-compilation warning can be issued by \pkg{zref-check} (according to the
-package's options), so that the problem can be easily identified and
-corrected.  Evidently, this does not fully automate the typeset results, but
-it does automate the checking that the reference context is the intended one.
 
-And to make use of this feature, one does not need to rely on compiling the
-document, make the reference according to the current state of things, and
-then adjusting it over and over, as the document gets edited.  This feature is
-best used within a certain workflow, which starts by making a standard
-reference with a page reference, but adding the \texttt{pagegap} check to it:
-\begin{zcexample}
-\zcref{fig:1} on \zcpageref[check=pagegap]{fig:1}
-\end{zcexample}
-
-During the initial editing of the document, this reference will be formally
-correct, even if less than ideal in certain cases.  At a certain point, when
-you choose to give finishing touches to your document, then you can cater for
-\pkg{zref-check}'s warnings (the package has options to control them, for
-example, enabling warnings only when the \opt{draft} option is not in use).
-The \texttt{pagegap} check will pass if there's one or more pages between the
-reference and the label, in which case, that reference is already what we
-want.  However, if the referenced object lies in the previous page, the same
-page, or the next page, the \texttt{pagegap} check will issue a warning.  Thus
-telling us if there's an opportunity, or need, to adjust the context
-information of the reference.  Then we can go with one of the below, according
-to the situation:
-\begin{zcexample}
-\zcref[check=thispage]{fig:1}
-\zcref[check=above,note={above}]{fig:1}
-\zcref[check=below,note={below}]{fig:1}
-\zcref[check=nextpage,note={on the following page}]{fig:1}
-\zcref[check=prevpage,note={on the preceding page}]{fig:1}
-\zcref[check=facing,note={on the facing page}]{fig:1}
-\end{zcexample}
-
-See \pkg{zref-check}'s documentation for details, and further available
-checks.
-
-
 \subsection{\cs{newtheorem}}
 
 Since \LaTeX{}'s \cs{newtheorem} allows users to create arbitrary numbered
@@ -2087,16 +2107,20 @@
 usage example.  The module also ensures proper \opt{currentcounter} values are
 in place for the display math environments, for which it uses environment
 hooks, and sets the font of equation references to \cs{upshape}, following
-\pkg{amsmath}'s \cs{eqref}.  Note, however, that \pkg{zref-clever} is not the
-only package to redefine \cs{ltx at label}, and compatibility problems may arise
-if this module is used with such packages or with document classes that do the
-same.  In case of trouble, you can load \pkg{zref-clever} with option
-\texttt{nocompat=amsmath} and either use the standard referencing system's
-facilities to refer to \pkg{amsmath}'s equations or check the code
-documentation for the technique used for this (which is pretty standard) and
-adapt it to your case.  Given that any trouble that may arise here is one of
-the proper ``timing'' of the redefinition, it should not be particularly
-complicated to make such adjustments.
+\pkg{amsmath}'s \cs{eqref}.  Finally, the module also provides a
+\texttt{subeq} property, for display math environments used inside the
+\env{subequations} environment, which can be used to refer to them directly
+with the \opt{ref} option, or to build terse ranges with the \opt{endrange}
+option.  Note that \pkg{zref-clever} is not the only package to redefine
+\cs{ltx at label}, and compatibility problems may arise if this module is used
+with such packages or with document classes that do the same.  In case of
+trouble, you can load \pkg{zref-clever} with option \texttt{nocompat=amsmath}
+and either use the standard referencing system's facilities to refer to
+\pkg{amsmath}'s equations or check the code documentation for the technique
+used for this (which is pretty standard) and adapt it to your case.  Given
+that any trouble that may arise here is one of the proper ``timing'' of the
+redefinition, it should not be particularly complicated to make such
+adjustments.
 
 \DescribeOption{mathtools} %
 \pkg{mathtools} has a feature to show the numbers only for those equations

Modified: trunk/Master/texmf-dist/source/latex/zref-clever/zref-clever.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/zref-clever/zref-clever.dtx	2022-02-07 21:40:38 UTC (rev 61934)
+++ trunk/Master/texmf-dist/source/latex/zref-clever/zref-clever.dtx	2022-02-07 21:40:53 UTC (rev 61935)
@@ -162,7 +162,7 @@
 %
 % Identify the package.
 %    \begin{macrocode}
-\ProvidesExplPackage {zref-clever} {2022-01-28} {0.2.0-alpha}
+\ProvidesExplPackage {zref-clever} {2022-02-07} {0.2.1-alpha}
   {Clever LaTeX cross-references based on zref}
 %    \end{macrocode}
 %
@@ -607,6 +607,11 @@
     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:.~
@@ -667,7 +672,7 @@
 %   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 val}
+%     \cs{@@_extract_default:Nnnn} \Arg{tl var}
 %     ~~\Arg{label} \Arg{prop} \Arg{default}
 %   \end{syntax}
 %    \begin{macrocode}
@@ -676,7 +681,7 @@
     \exp_args:NNNo \exp_args:NNo \tl_set:Nn #1
       { \zref at extractdefault {#2} {#3} {#4} }
   }
-\cs_generate_variant:Nn \@@_extract_default:Nnnn { NVnn }
+\cs_generate_variant:Nn \@@_extract_default:Nnnn { NVnn , Nnvn }
 %    \end{macrocode}
 % \end{macro}
 %
@@ -1103,16 +1108,22 @@
 % \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}.  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
+% \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.  In the language files
-% the ``unsetting'' behavior is less meaningful, since they only change any
-% variable if it is unset to start with, so that unsetting an unset variable
-% would be redundant.  However, for UI symmetry also in the language files
-% keys with no value should not be considered ``empty'' and boolean
-% \texttt{unset} values should exist.  They are just no-op.
+% a third value ``\texttt{unset}'' for this purpose.  And similarly for
+% ``choice'' options.  In the language files the ``unsetting'' behavior is
+% less meaningful, since they only change any variable if it is unset to start
+% with, so that unsetting an unset variable would be redundant.  However, for
+% UI symmetry also in the language files keys with no value should not be
+% considered ``empty'' and boolean \texttt{unset} values should exist.  They
+% are just no-op.
 %
 %
 % \begin{macro}
@@ -1191,6 +1202,7 @@
   {
     cap ,
     abbrev ,
+    rangetopair ,
   }
 %    \end{macrocode}
 % Only ``type names'' are ``necessarily type-specific'', which makes them
@@ -1880,6 +1892,225 @@
           } ,
       }
   }
+\keys_define:nn { zref-clever/langfile }
+  {
+    endrange .code:n =
+      {
+        \str_case:nnF {#1}
+          {
+            { ref }
+            {
+              \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 }
+                    }
+                    { }
+                  \@@_opt_tl_gset_if_new:cn
+                    {
+                      \@@_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 }
+                    }
+                    { }
+                  \@@_opt_tl_gset_if_new:cn
+                    {
+                      \@@_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_gset_if_new:cn
+                    {
+                      \@@_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_gset_if_new:cn
+                    {
+                      \@@_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_gset_if_new:cn
+                    {
+                      \@@_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_gset_if_new:cn
+                    {
+                      \@@_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_gset_if_new:cn
+                    {
+                      \@@_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_gset_if_new:cn
+                    {
+                      \@@_opt_varname_lang_type:eenn
+                        { \l_@@_setup_language_tl }
+                        { \l_@@_setup_type_tl }
+                        { endrangeprop } { tl }
+                    }
+                    { }
+                }
+            }
+
+            { unset }
+            { }
+          }
+          {
+            \tl_if_empty:nTF {#1}
+              {
+                \msg_info:nnn { zref-clever }
+                  { endrange-property-undefined } {#1}
+              }
+              {
+                \zref at 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}
+                      }
+                  }
+              }
+          }
+      } ,
+    endrange .value_required:n = true ,
+  }
 \seq_map_inline:Nn
   \c_@@_rf_opts_tl_type_names_seq
   {
@@ -2167,7 +2398,9 @@
 % 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 at ifpropundefined}: close it at the door.
+% \cs{zref at ifpropundefined}: close it at the door.  We must also control for
+% an empty value, since ``empty'' passes both \cs{zref at ifpropundefined} and
+% \cs{zref at ifrefcontainsprop}.
 %
 %
 %    \begin{macrocode}
@@ -2176,12 +2409,21 @@
   {
     ref .code:n =
       {
-        \zref at ifpropundefined {#1}
+        \tl_if_empty:nTF {#1}
           {
-            \msg_warning:nnn { zref-clever } { zref-property-undefined } {#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} }
+          {
+            \zref at 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 ,
@@ -2286,9 +2528,445 @@
 %
 %
 %
-% \subsubsection*{\opt{range} option}
+% \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 at 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 at 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 }
+            {
+              \tl_clear:c
+                {
+                  \@@_opt_varname_general:nn
+                    { endrangefunc } { tl }
+                }
+              \tl_clear:c
+                {
+                  \@@_opt_varname_general:nn
+                    { endrangeprop } { tl }
+                }
+            }
+
+            { stripprefix }
+            {
+              \tl_set:cn
+                {
+                  \@@_opt_varname_general:nn
+                    { endrangefunc } { tl }
+                }
+                { @@_get_endrange_stripprefix }
+              \tl_clear:c
+                {
+                  \@@_opt_varname_general:nn
+                    { endrangeprop } { tl }
+                }
+            }
+
+            { pagecomp }
+            {
+              \tl_set:cn
+                {
+                  \@@_opt_varname_general:nn
+                    { endrangefunc } { tl }
+                }
+                { @@_get_endrange_pagecomp }
+              \tl_clear:c
+                {
+                  \@@_opt_varname_general:nn
+                    { endrangeprop } { tl }
+                }
+            }
+
+            { pagecomp2 }
+            {
+              \tl_set:cn
+                {
+                  \@@_opt_varname_general:nn
+                    { endrangefunc } { tl }
+                }
+                { @@_get_endrange_pagecomptwo }
+              \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 at ifpropundefined {#1}
+                  {
+                    \msg_warning:nnn { zref-clever }
+                      { endrange-property-undefined } {#1}
+                  }
+                  {
+                    \tl_set:cn
+                      {
+                        \@@_opt_varname_general:nn
+                          { endrangefunc } { tl }
+                      }
+                      { @@_get_endrange_property }
+                    \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 at ifrefcontainsprop {#2} { \l_@@_ref_property_tl }
+          {
+            \@@_extract_default:Nnvn #3
+              {#2} { l_@@_ref_property_tl } { }
+          }
+          { \tl_set:Nn #3 { zc at missingproperty } }
+      }
+      {
+        \zref at 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 at pgfmt } { }
+                          }
+                          {
+                            \@@_extract_unexp:nnn
+                              {#2} { zc at pgfmt } { }
+                          }
+                          { \bool_set_true:N \l_tmpa_bool }
+                      }
+                      {
+                        \exp_args:Nxx \tl_if_eq:nnT
+                          {
+                            \@@_extract_unexp:nnn
+                              {#1} { zc at counter } { }
+                          }
+                          {
+                            \@@_extract_unexp:nnn
+                              {#2} { zc at counter } { }
+                          }
+                          {
+                            \exp_args:Nxx \tl_if_eq:nnT
+                              {
+                                \@@_extract_unexp:nnn
+                                  {#1} { zc at enclval } { }
+                              }
+                              {
+                                \@@_extract_unexp:nnn
+                                  {#2} { zc at 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 at ifrefcontainsprop
+                      {#2} { \l_@@_ref_property_tl }
+                      {
+                        \@@_extract_default:Nnvn \l_tmpb_tl
+                          {#2} { l_@@_ref_property_tl } { }
+                      }
+                      { \tl_set:Nn \l_tmpb_tl { zc at missingproperty } }
+                  }
+                \exp_args:NNNV
+                  \group_end:
+                  \tl_set:Nn #3 \l_tmpb_tl
+              }
+              {
+                \@@_extract_default:Nnvn #3
+                  {#2} { l_@@_endrangeprop_tl } { }
+              }
+          }
+          {
+            \zref at ifrefcontainsprop {#2} { \l_@@_ref_property_tl }
+              {
+                \@@_extract_default:Nnvn #3
+                  {#2} { l_@@_ref_property_tl } { }
+              }
+              { \tl_set:Nn #3 { zc at 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 at 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 at 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 at 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 at 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 at 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 at 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{c_@@_rf_opts_bool_maybe_type_specific_seq}.
+%
+%    \begin{macrocode}
 \bool_new:N \l_@@_typeset_range_bool
 \keys_define:nn { zref-clever/reference }
   {
@@ -2753,6 +3431,28 @@
 %    \end{macrocode}
 %
 %
+% \subsubsection*{\opt{vario} option}
+%
+%    \begin{macrocode}
+\keys_define:nn { zref-clever/reference }
+  {
+    vario .code:n = { \RequirePackage { zref-vario } } ,
+    vario .value_forbidden:n = true ,
+  }
+\AddToHook { begindocument }
+  {
+    \keys_define:nn { zref-clever/reference }
+      {
+        vario .code:n =
+          {
+            \msg_warning:nnn { zref-clever }
+              { option-preamble-only } { vario }
+          }
+      }
+  }
+%    \end{macrocode}
+%
+%
 % \subsubsection*{\opt{note} option}
 %
 %    \begin{macrocode}
@@ -3244,6 +3944,7 @@
   {
     \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}
@@ -3292,6 +3993,118 @@
   }
 \keys_define:nn { zref-clever/typesetup }
   {
+    endrange .code:n =
+      {
+        \str_case:nnF {#1}
+          {
+            { ref }
+            {
+              \tl_clear:c
+                {
+                  \@@_opt_varname_type:enn
+                    { \l_@@_setup_type_tl } { endrangefunc } { tl }
+                }
+              \tl_clear:c
+                {
+                  \@@_opt_varname_type:enn
+                    { \l_@@_setup_type_tl } { endrangeprop } { tl }
+                }
+            }
+
+            { stripprefix }
+            {
+              \tl_set:cn
+                {
+                  \@@_opt_varname_type:enn
+                    { \l_@@_setup_type_tl } { endrangefunc } { tl }
+                }
+                { @@_get_endrange_stripprefix }
+              \tl_clear:c
+                {
+                  \@@_opt_varname_type:enn
+                    { \l_@@_setup_type_tl } { endrangeprop } { tl }
+                }
+            }
+
+            { pagecomp }
+            {
+              \tl_set:cn
+                {
+                  \@@_opt_varname_type:enn
+                    { \l_@@_setup_type_tl } { endrangefunc } { tl }
+                }
+                { @@_get_endrange_pagecomp }
+              \tl_clear:c
+                {
+                  \@@_opt_varname_type:enn
+                    { \l_@@_setup_type_tl } { endrangeprop } { tl }
+                }
+            }
+
+            { pagecomp2 }
+            {
+              \tl_set:cn
+                {
+                  \@@_opt_varname_type:enn
+                    { \l_@@_setup_type_tl } { endrangefunc } { tl }
+                }
+                { @@_get_endrange_pagecomptwo }
+              \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 at ifpropundefined {#1}
+                  {
+                    \msg_warning:nnn { zref-clever }
+                      { endrange-property-undefined } {#1}
+                  }
+                  {
+                    \tl_set:cn
+                      {
+                        \@@_opt_varname_type:enn
+                          { \l_@@_setup_type_tl }
+                          { endrangefunc } { tl }
+                      }
+                      { @@_get_endrange_property }
+                    \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.
@@ -3627,6 +4440,247 @@
   }
 \keys_define:nn { zref-clever/langsetup }
   {
+    endrange .code:n =
+      {
+        \str_case:nnF {#1}
+          {
+            { ref }
+            {
+              \tl_if_empty:NTF \l_@@_setup_type_tl
+                {
+                  \tl_gclear:c
+                    {
+                      \@@_opt_varname_lang_default:enn
+                        { \l_@@_setup_language_tl }
+                        { endrangefunc } { tl }
+                    }
+                  \tl_gclear:c
+                    {
+                      \@@_opt_varname_lang_default:enn
+                        { \l_@@_setup_language_tl }
+                        { endrangeprop } { tl }
+                    }
+                }
+                {
+                  \tl_gclear:c
+                    {
+                      \@@_opt_varname_lang_type:eenn
+                        { \l_@@_setup_language_tl }
+                        { \l_@@_setup_type_tl }
+                        { endrangefunc } { tl }
+                    }
+                  \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
+                {
+                  \tl_gset:cn
+                    {
+                      \@@_opt_varname_lang_default:enn
+                        { \l_@@_setup_language_tl }
+                        { endrangefunc } { tl }
+                    }
+                    { @@_get_endrange_stripprefix }
+                  \tl_gclear:c
+                    {
+                      \@@_opt_varname_lang_default:enn
+                        { \l_@@_setup_language_tl }
+                        { endrangeprop } { tl }
+                    }
+                }
+                {
+                  \tl_gset:cn
+                    {
+                      \@@_opt_varname_lang_type:eenn
+                        { \l_@@_setup_language_tl }
+                        { \l_@@_setup_type_tl }
+                        { endrangefunc } { tl }
+                    }
+                    { @@_get_endrange_stripprefix }
+                  \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
+                {
+                  \tl_gset:cn
+                    {
+                      \@@_opt_varname_lang_default:enn
+                        { \l_@@_setup_language_tl }
+                        { endrangefunc } { tl }
+                    }
+                    { @@_get_endrange_pagecomp }
+                  \tl_gclear:c
+                    {
+                      \@@_opt_varname_lang_default:enn
+                        { \l_@@_setup_language_tl }
+                        { endrangeprop } { tl }
+                    }
+                }
+                {
+                  \tl_gset:cn
+                    {
+                      \@@_opt_varname_lang_type:eenn
+                        { \l_@@_setup_language_tl }
+                        { \l_@@_setup_type_tl }
+                        { endrangefunc } { tl }
+                    }
+                    { @@_get_endrange_pagecomp }
+                  \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
+                {
+                  \tl_gset:cn
+                    {
+                      \@@_opt_varname_lang_default:enn
+                        { \l_@@_setup_language_tl }
+                        { endrangefunc } { tl }
+                    }
+                    { @@_get_endrange_pagecomptwo }
+                  \tl_gclear:c
+                    {
+                      \@@_opt_varname_lang_default:enn
+                        { \l_@@_setup_language_tl }
+                        { endrangeprop } { tl }
+                    }
+                }
+                {
+                  \tl_gset:cn
+                    {
+                      \@@_opt_varname_lang_type:eenn
+                        { \l_@@_setup_language_tl }
+                        { \l_@@_setup_type_tl }
+                        { endrangefunc } { tl }
+                    }
+                    { @@_get_endrange_pagecomptwo }
+                  \tl_gclear:c
+                    {
+                      \@@_opt_varname_lang_type:eenn
+                        { \l_@@_setup_language_tl }
+                        { \l_@@_setup_type_tl }
+                        { endrangeprop } { tl }
+                    }
+                }
+            }
+
+            { unset }
+            {
+              \tl_if_empty:NTF \l_@@_setup_type_tl
+                {
+                  \@@_opt_tl_gunset:c
+                    {
+                      \@@_opt_varname_lang_default:enn
+                        { \l_@@_setup_language_tl }
+                        { endrangefunc } { tl }
+                    }
+                  \@@_opt_tl_gunset:c
+                    {
+                      \@@_opt_varname_lang_default:enn
+                        { \l_@@_setup_language_tl }
+                        { endrangeprop } { tl }
+                    }
+                }
+                {
+                  \@@_opt_tl_gunset:c
+                    {
+                      \@@_opt_varname_lang_type:eenn
+                        { \l_@@_setup_language_tl }
+                        { \l_@@_setup_type_tl }
+                        { endrangefunc } { tl }
+                    }
+                  \@@_opt_tl_gunset: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 at ifpropundefined {#1}
+                  {
+                    \msg_warning:nnn { zref-clever }
+                      { endrange-property-undefined } {#1}
+                  }
+                  {
+                    \tl_if_empty:NTF \l_@@_setup_type_tl
+                      {
+                        \tl_gset:cn
+                          {
+                            \@@_opt_varname_lang_default:enn
+                              { \l_@@_setup_language_tl }
+                              { endrangefunc } { tl }
+                          }
+                          { @@_get_endrange_property }
+                        \tl_gset:cn
+                          {
+                            \@@_opt_varname_lang_default:enn
+                              { \l_@@_setup_language_tl }
+                              { endrangeprop } { tl }
+                          }
+                          {#1}
+                      }
+                      {
+                        \tl_gset:cn
+                          {
+                            \@@_opt_varname_lang_type:eenn
+                              { \l_@@_setup_language_tl }
+                              { \l_@@_setup_type_tl }
+                              { endrangefunc } { tl }
+                          }
+                          { @@_get_endrange_property }
+                        \tl_gset:cn
+                          {
+                            \@@_opt_varname_lang_type:eenn
+                              { \l_@@_setup_language_tl }
+                              { \l_@@_setup_type_tl }
+                              { endrangeprop } { tl }
+                          }
+                          {#1}
+                      }
+                  }
+              }
+          }
+      } ,
+    endrange .value_required:n = true ,
+  }
+\keys_define:nn { zref-clever/langsetup }
+  {
     refpre .code:n =
       {
         % NOTE Option deprecated in 2022-01-10 for v0.1.2-alpha.
@@ -4207,7 +5261,7 @@
     \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.
+    % from `zref-xr') and, if so, sort based on that.
     \tl_if_eq:NNF
       \l_@@_label_extdoc_a_tl
       \l_@@_label_extdoc_b_tl
@@ -4589,6 +5643,8 @@
 %     \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 ,
 %   }
@@ -4597,6 +5653,8 @@
 \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}
@@ -4614,8 +5672,11 @@
 %     \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.
@@ -4630,8 +5691,11 @@
 \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}
 %
@@ -4692,11 +5756,13 @@
     \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).
@@ -4771,9 +5837,9 @@
           }
 
         % Handle warnings in case of reference or type undefined.
-        % Test: 'zc-typeset01.lvt': "Typeset refs: warn ref undefined"
+        % Test: `zc-typeset01.lvt': "Typeset refs: warn ref undefined"
         \zref at refused { \l_@@_label_a_tl }
-        % Test: 'zc-typeset01.lvt': "Typeset refs: warn missing type"
+        % Test: `zc-typeset01.lvt': "Typeset refs: warn missing type"
         \zref at ifrefundefined { \l_@@_label_a_tl }
           {}
           {
@@ -4825,6 +5891,14 @@
               { \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 }
@@ -4833,6 +5907,10 @@
               { \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 }
@@ -4910,7 +5988,7 @@
       {
         % 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"
+        % Test: `zc-typeset01.lvt': "Last of type: single"
         { 0 }
         {
           \tl_set:NV \l_@@_type_first_label_tl
@@ -4923,7 +6001,7 @@
         }
 
         % The last is the second: we have a pair (if not repeated).
-        % Test: 'zc-typeset01.lvt': "Last of type: pair"
+        % Test: `zc-typeset01.lvt': "Last of type: pair"
         { 1 }
         {
           \int_compare:nNnTF { \l_@@_range_same_count_int } = { 1 }
@@ -4951,7 +6029,7 @@
         \int_case:nnF { \l_@@_range_count_int }
           {
             % There was no range going on.
-            % Test: 'zc-typeset01.lvt': "Last of type: not range"
+            % Test: `zc-typeset01.lvt': "Last of type: not range"
             { 0 }
             {
               \int_compare:nNnTF { \l_@@_ref_count_int } < { 2 }
@@ -4973,15 +6051,15 @@
                 }
             }
             % Last in the range is also the second in it.
-            % Test: 'zc-typeset01.lvt': "Last of type: pair in sequence"
+            % Test: `zc-typeset01.lvt': "Last of type: pair in sequence"
             { 1 }
             {
               \int_compare:nNnTF
                 { \l_@@_range_same_count_int } = { 1 }
                 {
-                  % We know `range_beg_label' is not empty, since this is the
-                  % second element in the range, but the third or more in the
-                  % type list.
+                  % 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
@@ -5017,13 +6095,13 @@
               }
               {
                 % Repetition, not a range.
-                % Test: 'zc-typeset01.lvt': "Last of type: range to one"
+                % Test: `zc-typeset01.lvt': "Last of type: range to one"
                 { 0 }
                 {
-                  % If `range_beg_label' is empty, it means it was also the
-                  % first of the type, and hence its typesetting was already
-                  % handled, and we just have to set refbounds.
-                  \tl_if_empty:VTF \l_@@_range_beg_label_tl
+                  % 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
@@ -5055,11 +6133,11 @@
                 }
                 % 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"
+                % Test: `zc-typeset01.lvt': "Last of type: range to pair"
                 { 1 }
                 {
                   % Ditto.
-                  \tl_if_empty:VTF \l_@@_range_beg_label_tl
+                  \bool_if:NTF \l_@@_range_beg_is_first_bool
                     {
                       \seq_set_eq:NN \l_@@_type_first_refbounds_seq
                         \l_@@_refbounds_first_pb_seq
@@ -5091,9 +6169,9 @@
               }
               {
                 % An actual range.
-                % Test: 'zc-typeset01.lvt': "Last of type: range"
+                % Test: `zc-typeset01.lvt': "Last of type: range"
                 % Ditto.
-                \tl_if_empty:VTF \l_@@_range_beg_label_tl
+                \bool_if:NTF \l_@@_range_beg_is_first_bool
                   {
                     \seq_set_eq:NN \l_@@_type_first_refbounds_seq
                       \l_@@_refbounds_first_rb_seq
@@ -5127,12 +6205,31 @@
                           }
                       }
                   }
-                \tl_put_right:Nx \l_@@_typeset_queue_curr_tl
+                \bool_lazy_and:nnTF
+                  { ! \tl_if_empty_p:N \l_@@_endrangefunc_tl }
+                  { \cs_if_exist_p:c { \l_@@_endrangefunc_tl :VVN } }
                   {
-                    \exp_not:V \l_@@_rangesep_tl
-                    \@@_get_ref:VN \l_@@_label_a_tl
-                      \l_@@_refbounds_last_re_seq
+                    \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
+                      }
+                  }
               }
           }
       }
@@ -5154,15 +6251,18 @@
           }
           {
             \bool_set_false:N \l_@@_next_maybe_range_bool
-            \zref at ifrefundefined { \l_@@_type_first_label_tl }
-              { }
+            \bool_if:NT \l_@@_rangetopair_bool
               {
-                \@@_labels_in_sequence:nn
-                  { \l_@@_type_first_label_tl }
-                  { \l_@@_label_a_tl }
+                \zref at 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"
+            % 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
@@ -5176,12 +6276,35 @@
                 \bool_set_true:N \l_@@_type_first_refbounds_set_bool
               }
               {
-                \tl_set:Nx \l_@@_typeset_queue_curr_tl
+                \bool_lazy_and:nnTF
+                  { ! \tl_if_empty_p:N \l_@@_endrangefunc_tl }
+                  { \cs_if_exist_p:c { \l_@@_endrangefunc_tl :VVN } }
                   {
-                    \exp_not:V \l_@@_rangesep_tl
-                    \@@_get_ref:VN \l_@@_label_a_tl
-                      \l_@@_refbounds_last_re_seq
+                    % 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
@@ -5210,7 +6333,7 @@
       {
         \bool_if:NTF \l_@@_typeset_ref_bool
           {
-            % Test: 'zc-typeset01.lvt': "Last of type: option typeset ref"
+            % 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
@@ -5220,7 +6343,7 @@
           {
             \bool_if:NTF \l_@@_typeset_name_bool
               {
-                % Test: 'zc-typeset01.lvt': "Last of type: option typeset name"
+                % 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
@@ -5227,7 +6350,7 @@
                       {
                         \exp_not:N \group_begin:
                         \exp_not:V \l_@@_namefont_tl
-                        % It's two '@s', but escaped for DocStrip.
+                        % It's two `@s', but escaped for DocStrip.
                         \exp_not:N \hyper@@@@link
                           {
                             \@@_extract_url_unexp:V
@@ -5254,7 +6377,7 @@
                 % 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"
+                % Test: `zc-typeset01.lvt': "Last of type: option typeset none"
                 \tl_put_left:Nx \l_@@_typeset_queue_curr_tl
                   { \@@_get_ref_first: }
               }
@@ -5280,11 +6403,11 @@
         \int_case:nnF { \l_@@_type_count_int }
           {
             % Single type.
-            % Test: 'zc-typeset01.lvt': "Last of type: 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"
+            % Test: `zc-typeset01.lvt': "Last of type: pair of types"
             { 1 }
             {
               \l_@@_tpairsep_tl
@@ -5293,7 +6416,7 @@
           }
           {
             % Last in list of types.
-            % Test: 'zc-typeset01.lvt': "Last of type: list of types"
+            % Test: `zc-typeset01.lvt': "Last of type: list of types"
             \l_@@_tlastsep_tl
             \l_@@_typeset_queue_curr_tl
           }
@@ -5314,11 +6437,13 @@
         \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
       }
   }
@@ -5356,13 +6481,16 @@
           \l_@@_label_type_a_tl
         \int_incr:N \l_@@_ref_count_int
 
-        % If the next label may be part of a range, we set `range_beg_label'
-        % to "empty" (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"
+        % 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
           {
-            \tl_clear:N \l_@@_range_beg_label_tl
+            \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 }
@@ -5379,6 +6507,7 @@
                 % 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 }
@@ -5396,7 +6525,7 @@
             \int_case:nnF { \l_@@_range_count_int }
               {
                 % There was no range going on.
-                % Test: 'zc-typeset01.lvt': "Not last of type: no range"
+                % Test: `zc-typeset01.lvt': "Not last of type: no range"
                 { 0 }
                 {
                   \int_incr:N \l_@@_ref_count_int
@@ -5410,11 +6539,11 @@
                 % 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"
+                % Test: `zc-typeset01.lvt': "Not last of type: range pair to one"
+                % Test: `zc-typeset01.lvt': "Not last of type: range pair"
                 { 1 }
                 {
-                  \tl_if_empty:VTF \l_@@_range_beg_label_tl
+                  \bool_if:NTF \l_@@_range_beg_is_first_bool
                     {
                       \seq_set_eq:NN \l_@@_type_first_refbounds_seq
                         \l_@@_refbounds_first_seq
@@ -5456,10 +6585,10 @@
                     \l_@@_range_same_count_int
                   }
                   {
-                    % Test: 'zc-typeset01.lvt': "Not last of type: range to one"
+                    % Test: `zc-typeset01.lvt': "Not last of type: range to one"
                     { 0 }
                     {
-                      \tl_if_empty:VTF \l_@@_range_beg_label_tl
+                      \bool_if:NTF \l_@@_range_beg_is_first_bool
                         {
                           \seq_set_eq:NN
                             \l_@@_type_first_refbounds_seq
@@ -5478,10 +6607,10 @@
                             }
                         }
                     }
-                    % Test: 'zc-typeset01.lvt': "Not last of type: range to pair"
+                    % Test: `zc-typeset01.lvt': "Not last of type: range to pair"
                     { 1 }
                     {
-                      \tl_if_empty:VTF \l_@@_range_beg_label_tl
+                      \bool_if:NTF \l_@@_range_beg_is_first_bool
                         {
                           \seq_set_eq:NN
                             \l_@@_type_first_refbounds_seq
@@ -5509,8 +6638,8 @@
                     }
                   }
                   {
-                    % Test: 'zc-typeset01.lvt': "Not last of type: range"
-                    \tl_if_empty:VTF \l_@@_range_beg_label_tl
+                    % 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
@@ -5529,16 +6658,35 @@
                           }
                       }
                     % 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
+                    % 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'.
-                    \tl_put_right:Nx \l_@@_typeset_queue_curr_tl
+                    % 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 } }
                       {
-                        \exp_not:V \l_@@_rangesep_tl
-                        \@@_get_ref:VN \l_@@_label_a_tl
-                          \l_@@_refbounds_mid_re_seq
+                        \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
+                          }
+                      }
                   }
               }
             % Reset counters.
@@ -5599,7 +6747,8 @@
 % \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:}.
+%   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}
@@ -5648,6 +6797,53 @@
 %    \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 at missingproperty }
+      { \@@_ref_default: }
+      {
+        \bool_if:nTF
+          {
+            \l_@@_hyperlink_bool &&
+            ! \l_@@_link_star_bool
+          }
+          {
+            \exp_not:N \group_begin:
+            \exp_not:V \l_@@_reffont_tl
+            \seq_item:Nn #3 { 1 }
+            % It's two `@s', but escaped for DocStrip.
+            \exp_not:N \hyper@@@@link
+              { \@@_extract_url_unexp:n {#1} }
+              { \@@_extract_unexp:nnn {#1} { anchor } { } }
+              {
+                \seq_item:Nn #3 { 2 }
+                \exp_not:n {#2}
+                \seq_item:Nn #3 { 3 }
+              }
+            \seq_item:Nn #3 { 4 }
+            \exp_not:N \group_end:
+          }
+          {
+            \exp_not:N \group_begin:
+            \exp_not:V \l_@@_reffont_tl
+            \seq_item:Nn #3 { 1 }
+            \seq_item:Nn #3 { 2 }
+            \exp_not:n {#2}
+            \seq_item:Nn #3 { 3 }
+            \seq_item:Nn #3 { 4 }
+            \exp_not:N \group_end:
+          }
+      }
+  }
+\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,
@@ -5738,7 +6934,7 @@
                     \exp_not:V \l_@@_reffont_tl
                     \seq_item:Nn
                       \l_@@_type_first_refbounds_seq { 1 }
-                    % It's two '@s', but escaped for DocStrip.
+                    % It's two `@s', but escaped for DocStrip.
                     \exp_not:N \hyper@@@@link
                       {
                         \@@_extract_url_unexp:V
@@ -6071,14 +7267,9 @@
 %    \begin{macrocode}
 \cs_new_protected:Npn \@@_labels_in_sequence:nn #1#2
   {
-    \@@_extract_default:Nnnn \l_@@_label_extdoc_a_tl
-      {#1} { externaldocument } { }
-    \@@_extract_default:Nnnn \l_@@_label_extdoc_b_tl
-      {#2} { externaldocument } { }
-
-    \tl_if_eq:NNT
-      \l_@@_label_extdoc_a_tl
-      \l_@@_label_extdoc_b_tl
+    \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 }
           {
@@ -6123,8 +7314,14 @@
                             =
                           { \@@_extract:nnn {#2} { zc at cntval } { -1 } }
                           {
-                            \bool_set_true:N
-                              \l_@@_next_maybe_range_bool
+%    \end{macrocode}
+% If \texttt{zc at counter}s are equal, \texttt{zc at enclval}s are equal, and
+% \texttt{zc at 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}
@@ -6136,6 +7333,8 @@
                               }
                               {
                                 \bool_set_true:N
+                                  \l_@@_next_maybe_range_bool
+                                \bool_set_true:N
                                   \l_@@_next_is_same_bool
                               }
                           }
@@ -6720,9 +7919,10 @@
 % 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{\\begin\{subequations\}}, to refer to the
-% parent equation).
+% 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
@@ -6736,6 +7936,7 @@
                 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
@@ -6750,8 +7951,12 @@
 % 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.
+% 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 at newprop { subeq } { \alph { equation } }
         \clist_map_inline:nn
           {
             equation ,
@@ -6771,7 +7976,11 @@
           }
           {
             \AddToHook { env / #1 / begin }
-              { \@@_zcsetup:n { currentcounter = equation } }
+              {
+                \@@_zcsetup:n { currentcounter = equation }
+                \bool_if:NT \l_@@_amsmath_subequations_bool
+                  { \zref at localaddprop \ZREF at mainlist { subeq } }
+              }
           }
 %    \end{macrocode}
 % And a last touch of care for \pkg{amsmath}'s refinements: make the equation
@@ -6867,6 +8076,7 @@
 % 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
@@ -6880,7 +8090,11 @@
                 currentcounter = parentequation ,
                 countertype = { parentequation = equation } ,
               }
+            \bool_set_true:N \l_@@_breqn_dgroup_bool
           }
+        \zref at ifpropundefined { subeq }
+          { \zref at newprop { subeq } { \alph { equation } } }
+          { }
         \clist_map_inline:nn
           {
             dmath ,
@@ -6889,7 +8103,11 @@
           }
           {
             \AddToHook { env / #1 / begin }
-              { \@@_zcsetup:n { currentcounter = equation } }
+              {
+                \@@_zcsetup:n { currentcounter = equation }
+                \bool_if:NT \l_@@_breqn_dgroup_bool
+                  { \zref at localaddprop \ZREF at mainlist { subeq } }
+              }
           }
         \msg_info:nnn { zref-clever } { compat-package } { breqn }
       }
@@ -7154,6 +8372,7 @@
   Name-pl = Pages ,
   name-pl = pages ,
   rangesep = {\textendash} ,
+  rangetopair = false ,
 
 type = line ,
   Name-sg = Line ,
@@ -7436,6 +8655,7 @@
     Name-sg = Seite ,
     Name-pl = Seiten ,
   rangesep = {\textendash} ,
+  rangetopair = false ,
 
 type = line ,
   gender = f ,
@@ -7853,6 +9073,7 @@
   Name-pl = Pages ,
   name-pl = pages ,
   rangesep = {-} ,
+  rangetopair = false ,
 
 type = line ,
   gender = f ,
@@ -8094,6 +9315,7 @@
   Name-pl = Páginas ,
   name-pl = páginas ,
   rangesep = {\textendash} ,
+  rangetopair = false ,
 
 type = line ,
   gender = f ,
@@ -8333,6 +9555,7 @@
   Name-pl = Páginas ,
   name-pl = páginas ,
   rangesep = {\textendash} ,
+  rangetopair = false ,
 
 type = line ,
   gender = f ,
@@ -8566,6 +9789,7 @@
   Name-pl = Pagina's ,
   name-pl = pagina's ,
   rangesep = {\textendash} ,
+  rangetopair = false ,
 
 type = line ,
   gender = m ,

Modified: trunk/Master/texmf-dist/tex/latex/zref-clever/zref-clever-dutch.lang
===================================================================
--- trunk/Master/texmf-dist/tex/latex/zref-clever/zref-clever-dutch.lang	2022-02-07 21:40:38 UTC (rev 61934)
+++ trunk/Master/texmf-dist/tex/latex/zref-clever/zref-clever-dutch.lang	2022-02-07 21:40:53 UTC (rev 61935)
@@ -101,6 +101,7 @@
   Name-pl = Pagina's ,
   name-pl = pagina's ,
   rangesep = {\textendash} ,
+  rangetopair = false ,
 
 type = line ,
   gender = m ,

Modified: trunk/Master/texmf-dist/tex/latex/zref-clever/zref-clever-english.lang
===================================================================
--- trunk/Master/texmf-dist/tex/latex/zref-clever/zref-clever-english.lang	2022-02-07 21:40:38 UTC (rev 61934)
+++ trunk/Master/texmf-dist/tex/latex/zref-clever/zref-clever-english.lang	2022-02-07 21:40:53 UTC (rev 61935)
@@ -98,6 +98,7 @@
   Name-pl = Pages ,
   name-pl = pages ,
   rangesep = {\textendash} ,
+  rangetopair = false ,
 
 type = line ,
   Name-sg = Line ,

Modified: trunk/Master/texmf-dist/tex/latex/zref-clever/zref-clever-french.lang
===================================================================
--- trunk/Master/texmf-dist/tex/latex/zref-clever/zref-clever-french.lang	2022-02-07 21:40:38 UTC (rev 61934)
+++ trunk/Master/texmf-dist/tex/latex/zref-clever/zref-clever-french.lang	2022-02-07 21:40:53 UTC (rev 61935)
@@ -101,6 +101,7 @@
   Name-pl = Pages ,
   name-pl = pages ,
   rangesep = {-} ,
+  rangetopair = false ,
 
 type = line ,
   gender = f ,

Modified: trunk/Master/texmf-dist/tex/latex/zref-clever/zref-clever-german.lang
===================================================================
--- trunk/Master/texmf-dist/tex/latex/zref-clever/zref-clever-german.lang	2022-02-07 21:40:38 UTC (rev 61934)
+++ trunk/Master/texmf-dist/tex/latex/zref-clever/zref-clever-german.lang	2022-02-07 21:40:53 UTC (rev 61935)
@@ -157,6 +157,7 @@
     Name-sg = Seite ,
     Name-pl = Seiten ,
   rangesep = {\textendash} ,
+  rangetopair = false ,
 
 type = line ,
   gender = f ,

Modified: trunk/Master/texmf-dist/tex/latex/zref-clever/zref-clever-portuguese.lang
===================================================================
--- trunk/Master/texmf-dist/tex/latex/zref-clever/zref-clever-portuguese.lang	2022-02-07 21:40:38 UTC (rev 61934)
+++ trunk/Master/texmf-dist/tex/latex/zref-clever/zref-clever-portuguese.lang	2022-02-07 21:40:53 UTC (rev 61935)
@@ -105,6 +105,7 @@
   Name-pl = Páginas ,
   name-pl = páginas ,
   rangesep = {\textendash} ,
+  rangetopair = false ,
 
 type = line ,
   gender = f ,

Modified: trunk/Master/texmf-dist/tex/latex/zref-clever/zref-clever-spanish.lang
===================================================================
--- trunk/Master/texmf-dist/tex/latex/zref-clever/zref-clever-spanish.lang	2022-02-07 21:40:38 UTC (rev 61934)
+++ trunk/Master/texmf-dist/tex/latex/zref-clever/zref-clever-spanish.lang	2022-02-07 21:40:53 UTC (rev 61935)
@@ -101,6 +101,7 @@
   Name-pl = Páginas ,
   name-pl = páginas ,
   rangesep = {\textendash} ,
+  rangetopair = false ,
 
 type = line ,
   gender = f ,

Modified: trunk/Master/texmf-dist/tex/latex/zref-clever/zref-clever.sty
===================================================================
--- trunk/Master/texmf-dist/tex/latex/zref-clever/zref-clever.sty	2022-02-07 21:40:38 UTC (rev 61934)
+++ trunk/Master/texmf-dist/tex/latex/zref-clever/zref-clever.sty	2022-02-07 21:40:53 UTC (rev 61935)
@@ -53,7 +53,7 @@
       }%
     \endinput
   }%
-\ProvidesExplPackage {zref-clever} {2022-01-28} {0.2.0-alpha}
+\ProvidesExplPackage {zref-clever} {2022-02-07} {0.2.1-alpha}
   {Clever LaTeX cross-references based on zref}
 \RequirePackage { zref-base }
 \RequirePackage { zref-user }
@@ -249,6 +249,11 @@
     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:.~
@@ -302,7 +307,7 @@
     \exp_args:NNNo \exp_args:NNo \tl_set:Nn #1
       { \zref at extractdefault {#2} {#3} {#4} }
   }
-\cs_generate_variant:Nn \__zrefclever_extract_default:Nnnn { NVnn }
+\cs_generate_variant:Nn \__zrefclever_extract_default:Nnnn { NVnn , Nnvn }
 \cs_new:Npn \__zrefclever_extract_unexp:nnn #1#2#3
   {
     \exp_args:NNo \exp_args:No
@@ -480,6 +485,7 @@
   {
     cap ,
     abbrev ,
+    rangetopair ,
   }
 \seq_const_from_clist:Nn
   \c__zrefclever_rf_opts_tl_type_names_seq
@@ -938,6 +944,225 @@
           } ,
       }
   }
+\keys_define:nn { zref-clever/langfile }
+  {
+    endrange .code:n =
+      {
+        \str_case:nnF {#1}
+          {
+            { ref }
+            {
+              \tl_if_empty:NTF \l__zrefclever_setup_type_tl
+                {
+                  \__zrefclever_opt_tl_gset_if_new:cn
+                    {
+                      \__zrefclever_opt_varname_lang_default:enn
+                        { \l__zrefclever_setup_language_tl }
+                        { endrangefunc } { tl }
+                    }
+                    { }
+                  \__zrefclever_opt_tl_gset_if_new:cn
+                    {
+                      \__zrefclever_opt_varname_lang_default:enn
+                        { \l__zrefclever_setup_language_tl }
+                        { endrangeprop } { tl }
+                    }
+                    { }
+                }
+                {
+                  \__zrefclever_opt_tl_gset_if_new:cn
+                    {
+                      \__zrefclever_opt_varname_lang_type:eenn
+                        { \l__zrefclever_setup_language_tl }
+                        { \l__zrefclever_setup_type_tl }
+                        { endrangefunc } { tl }
+                    }
+                    { }
+                  \__zrefclever_opt_tl_gset_if_new:cn
+                    {
+                      \__zrefclever_opt_varname_lang_type:eenn
+                        { \l__zrefclever_setup_language_tl }
+                        { \l__zrefclever_setup_type_tl }
+                        { endrangeprop } { tl }
+                    }
+                    { }
+                }
+            }
+
+            { stripprefix }
+            {
+              \tl_if_empty:NTF \l__zrefclever_setup_type_tl
+                {
+                  \__zrefclever_opt_tl_gset_if_new:cn
+                    {
+                      \__zrefclever_opt_varname_lang_default:enn
+                        { \l__zrefclever_setup_language_tl }
+                        { endrangefunc } { tl }
+                    }
+                    { __zrefclever_get_endrange_stripprefix }
+                  \__zrefclever_opt_tl_gset_if_new:cn
+                    {
+                      \__zrefclever_opt_varname_lang_default:enn
+                        { \l__zrefclever_setup_language_tl }
+                        { endrangeprop } { tl }
+                    }
+                    { }
+                }
+                {
+                  \__zrefclever_opt_tl_gset_if_new:cn
+                    {
+                      \__zrefclever_opt_varname_lang_type:eenn
+                        { \l__zrefclever_setup_language_tl }
+                        { \l__zrefclever_setup_type_tl }
+                        { endrangefunc } { tl }
+                    }
+                    { __zrefclever_get_endrange_stripprefix }
+                  \__zrefclever_opt_tl_gset_if_new:cn
+                    {
+                      \__zrefclever_opt_varname_lang_type:eenn
+                        { \l__zrefclever_setup_language_tl }
+                        { \l__zrefclever_setup_type_tl }
+                        { endrangeprop } { tl }
+                    }
+                    { }
+                }
+            }
+
+            { pagecomp }
+            {
+              \tl_if_empty:NTF \l__zrefclever_setup_type_tl
+                {
+                  \__zrefclever_opt_tl_gset_if_new:cn
+                    {
+                      \__zrefclever_opt_varname_lang_default:enn
+                        { \l__zrefclever_setup_language_tl }
+                        { endrangefunc } { tl }
+                    }
+                    { __zrefclever_get_endrange_pagecomp }
+                  \__zrefclever_opt_tl_gset_if_new:cn
+                    {
+                      \__zrefclever_opt_varname_lang_default:enn
+                        { \l__zrefclever_setup_language_tl }
+                        { endrangeprop } { tl }
+                    }
+                    { }
+                }
+                {
+                  \__zrefclever_opt_tl_gset_if_new:cn
+                    {
+                      \__zrefclever_opt_varname_lang_type:eenn
+                        { \l__zrefclever_setup_language_tl }
+                        { \l__zrefclever_setup_type_tl }
+                        { endrangefunc } { tl }
+                    }
+                    { __zrefclever_get_endrange_pagecomp }
+                  \__zrefclever_opt_tl_gset_if_new:cn
+                    {
+                      \__zrefclever_opt_varname_lang_type:eenn
+                        { \l__zrefclever_setup_language_tl }
+                        { \l__zrefclever_setup_type_tl }
+                        { endrangeprop } { tl }
+                    }
+                    { }
+                }
+            }
+
+            { pagecomp2 }
+            {
+              \tl_if_empty:NTF \l__zrefclever_setup_type_tl
+                {
+                  \__zrefclever_opt_tl_gset_if_new:cn
+                    {
+                      \__zrefclever_opt_varname_lang_default:enn
+                        { \l__zrefclever_setup_language_tl }
+                        { endrangefunc } { tl }
+                    }
+                    { __zrefclever_get_endrange_pagecomptwo }
+                  \__zrefclever_opt_tl_gset_if_new:cn
+                    {
+                      \__zrefclever_opt_varname_lang_default:enn
+                        { \l__zrefclever_setup_language_tl }
+                        { endrangeprop } { tl }
+                    }
+                    { }
+                }
+                {
+                  \__zrefclever_opt_tl_gset_if_new:cn
+                    {
+                      \__zrefclever_opt_varname_lang_type:eenn
+                        { \l__zrefclever_setup_language_tl }
+                        { \l__zrefclever_setup_type_tl }
+                        { endrangefunc } { tl }
+                    }
+                    { __zrefclever_get_endrange_pagecomptwo }
+                  \__zrefclever_opt_tl_gset_if_new:cn
+                    {
+                      \__zrefclever_opt_varname_lang_type:eenn
+                        { \l__zrefclever_setup_language_tl }
+                        { \l__zrefclever_setup_type_tl }
+                        { endrangeprop } { tl }
+                    }
+                    { }
+                }
+            }
+
+            { unset }
+            { }
+          }
+          {
+            \tl_if_empty:nTF {#1}
+              {
+                \msg_info:nnn { zref-clever }
+                  { endrange-property-undefined } {#1}
+              }
+              {
+                \zref at ifpropundefined {#1}
+                  {
+                    \msg_info:nnn { zref-clever }
+                      { endrange-property-undefined } {#1}
+                  }
+                  {
+                    \tl_if_empty:NTF \l__zrefclever_setup_type_tl
+                      {
+                        \__zrefclever_opt_tl_gset_if_new:cn
+                          {
+                            \__zrefclever_opt_varname_lang_default:enn
+                              { \l__zrefclever_setup_language_tl }
+                              { endrangefunc } { tl }
+                          }
+                          { __zrefclever_get_endrange_property }
+                        \__zrefclever_opt_tl_gset_if_new:cn
+                          {
+                            \__zrefclever_opt_varname_lang_default:enn
+                              { \l__zrefclever_setup_language_tl }
+                              { endrangeprop } { tl }
+                          }
+                          {#1}
+                      }
+                      {
+                        \__zrefclever_opt_tl_gset_if_new:cn
+                          {
+                            \__zrefclever_opt_varname_lang_type:eenn
+                              { \l__zrefclever_setup_language_tl }
+                              { \l__zrefclever_setup_type_tl }
+                              { endrangefunc } { tl }
+                          }
+                          { __zrefclever_get_endrange_property }
+                        \__zrefclever_opt_tl_gset_if_new:cn
+                          {
+                            \__zrefclever_opt_varname_lang_type:eenn
+                              { \l__zrefclever_setup_language_tl }
+                              { \l__zrefclever_setup_type_tl }
+                              { endrangeprop } { tl }
+                          }
+                          {#1}
+                      }
+                  }
+              }
+          }
+      } ,
+    endrange .value_required:n = true ,
+  }
 \seq_map_inline:Nn
   \c__zrefclever_rf_opts_tl_type_names_seq
   {
@@ -1185,12 +1410,21 @@
   {
     ref .code:n =
       {
-        \zref at ifpropundefined {#1}
+        \tl_if_empty:nTF {#1}
           {
-            \msg_warning:nnn { zref-clever } { zref-property-undefined } {#1}
+            \msg_warning:nnn { zref-clever }
+              { zref-property-undefined } {#1}
             \tl_set:Nn \l__zrefclever_ref_property_tl { default }
           }
-          { \tl_set:Nn \l__zrefclever_ref_property_tl {#1} }
+          {
+            \zref at ifpropundefined {#1}
+              {
+                \msg_warning:nnn { zref-clever }
+                  { zref-property-undefined } {#1}
+                \tl_set:Nn \l__zrefclever_ref_property_tl { default }
+              }
+              { \tl_set:Nn \l__zrefclever_ref_property_tl {#1} }
+          }
       } ,
     ref .initial:n = default ,
     ref .value_required:n = true ,
@@ -1258,6 +1492,349 @@
     nocomp .meta:n = { comp = false },
     nocomp .value_forbidden:n = true ,
   }
+\NewHook { zref-clever/endrange-setup }
+\keys_define:nn { zref-clever/reference }
+  {
+    endrange .code:n =
+      {
+        \str_case:nnF {#1}
+          {
+            { ref }
+            {
+              \tl_clear:c
+                {
+                  \__zrefclever_opt_varname_general:nn
+                    { endrangefunc } { tl }
+                }
+              \tl_clear:c
+                {
+                  \__zrefclever_opt_varname_general:nn
+                    { endrangeprop } { tl }
+                }
+            }
+
+            { stripprefix }
+            {
+              \tl_set:cn
+                {
+                  \__zrefclever_opt_varname_general:nn
+                    { endrangefunc } { tl }
+                }
+                { __zrefclever_get_endrange_stripprefix }
+              \tl_clear:c
+                {
+                  \__zrefclever_opt_varname_general:nn
+                    { endrangeprop } { tl }
+                }
+            }
+
+            { pagecomp }
+            {
+              \tl_set:cn
+                {
+                  \__zrefclever_opt_varname_general:nn
+                    { endrangefunc } { tl }
+                }
+                { __zrefclever_get_endrange_pagecomp }
+              \tl_clear:c
+                {
+                  \__zrefclever_opt_varname_general:nn
+                    { endrangeprop } { tl }
+                }
+            }
+
+            { pagecomp2 }
+            {
+              \tl_set:cn
+                {
+                  \__zrefclever_opt_varname_general:nn
+                    { endrangefunc } { tl }
+                }
+                { __zrefclever_get_endrange_pagecomptwo }
+              \tl_clear:c
+                {
+                  \__zrefclever_opt_varname_general:nn
+                    { endrangeprop } { tl }
+                }
+            }
+
+            { unset }
+            {
+              \__zrefclever_opt_tl_unset:c
+                {
+                  \__zrefclever_opt_varname_general:nn
+                    { endrangefunc } { tl }
+                }
+              \__zrefclever_opt_tl_unset:c
+                {
+                  \__zrefclever_opt_varname_general:nn
+                    { endrangeprop } { tl }
+                }
+            }
+          }
+          {
+            \tl_if_empty:nTF {#1}
+              {
+                \msg_warning:nnn { zref-clever }
+                  { endrange-property-undefined } {#1}
+              }
+              {
+                \zref at ifpropundefined {#1}
+                  {
+                    \msg_warning:nnn { zref-clever }
+                      { endrange-property-undefined } {#1}
+                  }
+                  {
+                    \tl_set:cn
+                      {
+                        \__zrefclever_opt_varname_general:nn
+                          { endrangefunc } { tl }
+                      }
+                      { __zrefclever_get_endrange_property }
+                    \tl_set:cn
+                      {
+                        \__zrefclever_opt_varname_general:nn
+                          { endrangeprop } { tl }
+                      }
+                      {#1}
+                  }
+              }
+          }
+      } ,
+    endrange .value_required:n = true ,
+  }
+\cs_new_protected:Npn \__zrefclever_get_endrange_property:nnN #1#2#3
+  {
+    \tl_if_empty:NTF \l__zrefclever_endrangeprop_tl
+      {
+        \zref at ifrefcontainsprop {#2} { \l__zrefclever_ref_property_tl }
+          {
+            \__zrefclever_extract_default:Nnvn #3
+              {#2} { l__zrefclever_ref_property_tl } { }
+          }
+          { \tl_set:Nn #3 { zc at missingproperty } }
+      }
+      {
+        \zref at ifrefcontainsprop {#2} { \l__zrefclever_endrangeprop_tl }
+          {
+            \bool_if:NTF \l__zrefclever_typeset_range_bool
+              {
+                \group_begin:
+                \bool_set_false:N \l_tmpa_bool
+                \exp_args:Nxx \tl_if_eq:nnT
+                  {
+                    \__zrefclever_extract_unexp:nnn
+                      {#1} { externaldocument } { }
+                  }
+                  {
+                    \__zrefclever_extract_unexp:nnn
+                      {#2} { externaldocument } { }
+                  }
+                  {
+                    \tl_if_eq:NnTF \l__zrefclever_ref_property_tl { page }
+                      {
+                        \exp_args:Nxx \tl_if_eq:nnT
+                          {
+                            \__zrefclever_extract_unexp:nnn
+                              {#1} { zc at pgfmt } { }
+                          }
+                          {
+                            \__zrefclever_extract_unexp:nnn
+                              {#2} { zc at pgfmt } { }
+                          }
+                          { \bool_set_true:N \l_tmpa_bool }
+                      }
+                      {
+                        \exp_args:Nxx \tl_if_eq:nnT
+                          {
+                            \__zrefclever_extract_unexp:nnn
+                              {#1} { zc at counter } { }
+                          }
+                          {
+                            \__zrefclever_extract_unexp:nnn
+                              {#2} { zc at counter } { }
+                          }
+                          {
+                            \exp_args:Nxx \tl_if_eq:nnT
+                              {
+                                \__zrefclever_extract_unexp:nnn
+                                  {#1} { zc at enclval } { }
+                              }
+                              {
+                                \__zrefclever_extract_unexp:nnn
+                                  {#2} { zc at enclval } { }
+                              }
+                              { \bool_set_true:N \l_tmpa_bool }
+                          }
+                      }
+                  }
+                \bool_if:NTF \l_tmpa_bool
+                  {
+                    \__zrefclever_extract_default:Nnvn \l_tmpb_tl
+                      {#2} { l__zrefclever_endrangeprop_tl } { }
+                  }
+                  {
+                    \zref at ifrefcontainsprop
+                      {#2} { \l__zrefclever_ref_property_tl }
+                      {
+                        \__zrefclever_extract_default:Nnvn \l_tmpb_tl
+                          {#2} { l__zrefclever_ref_property_tl } { }
+                      }
+                      { \tl_set:Nn \l_tmpb_tl { zc at missingproperty } }
+                  }
+                \exp_args:NNNV
+                  \group_end:
+                  \tl_set:Nn #3 \l_tmpb_tl
+              }
+              {
+                \__zrefclever_extract_default:Nnvn #3
+                  {#2} { l__zrefclever_endrangeprop_tl } { }
+              }
+          }
+          {
+            \zref at ifrefcontainsprop {#2} { \l__zrefclever_ref_property_tl }
+              {
+                \__zrefclever_extract_default:Nnvn #3
+                  {#2} { l__zrefclever_ref_property_tl } { }
+              }
+              { \tl_set:Nn #3 { zc at missingproperty } }
+          }
+      }
+  }
+\cs_generate_variant:Nn \__zrefclever_get_endrange_property:nnN { VVN }
+\cs_new_protected:Npn \__zrefclever_get_endrange_stripprefix:nnN #1#2#3
+  {
+    \zref at ifrefcontainsprop {#2} { \l__zrefclever_ref_property_tl }
+      {
+        \group_begin:
+        \UseHook { zref-clever/endrange-setup }
+        \tl_set:Nx \l_tmpa_tl
+          {
+            \__zrefclever_extract:nnn
+              {#1} { \l__zrefclever_ref_property_tl } { }
+          }
+        \tl_set:Nx \l_tmpb_tl
+          {
+            \__zrefclever_extract:nnn
+              {#2} { \l__zrefclever_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 at missingproperty } }
+  }
+\cs_generate_variant:Nn \__zrefclever_get_endrange_stripprefix:nnN { VVN }
+\prg_new_protected_conditional:Npnn
+  \__zrefclever_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
+  \__zrefclever_is_integer_rgx:n { V } { F , TF }
+\cs_new_protected:Npn \__zrefclever_get_endrange_pagecomp:nnN #1#2#3
+  {
+    \zref at ifrefcontainsprop {#2} { \l__zrefclever_ref_property_tl }
+      {
+        \group_begin:
+        \UseHook { zref-clever/endrange-setup }
+        \tl_set:Nx \l_tmpa_tl
+          {
+            \__zrefclever_extract:nnn
+              {#1} { \l__zrefclever_ref_property_tl } { }
+          }
+        \tl_set:Nx \l_tmpb_tl
+          {
+            \__zrefclever_extract:nnn
+              {#2} { \l__zrefclever_ref_property_tl } { }
+          }
+        \bool_set_false:N \l_tmpa_bool
+        \__zrefclever_is_integer_rgx:VTF \l_tmpa_tl
+          {
+            \__zrefclever_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 at missingproperty } }
+  }
+\cs_generate_variant:Nn \__zrefclever_get_endrange_pagecomp:nnN { VVN }
+\cs_new_protected:Npn \__zrefclever_get_endrange_pagecomptwo:nnN #1#2#3
+  {
+    \zref at ifrefcontainsprop {#2} { \l__zrefclever_ref_property_tl }
+      {
+        \group_begin:
+        \UseHook { zref-clever/endrange-setup }
+        \tl_set:Nx \l_tmpa_tl
+          {
+            \__zrefclever_extract:nnn
+              {#1} { \l__zrefclever_ref_property_tl } { }
+          }
+        \tl_set:Nx \l_tmpb_tl
+          {
+            \__zrefclever_extract:nnn
+              {#2} { \l__zrefclever_ref_property_tl } { }
+          }
+        \bool_set_false:N \l_tmpa_bool
+        \__zrefclever_is_integer_rgx:VTF \l_tmpa_tl
+          {
+            \__zrefclever_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 at missingproperty } }
+  }
+\cs_generate_variant:Nn \__zrefclever_get_endrange_pagecomptwo:nnN { VVN }
 \bool_new:N \l__zrefclever_typeset_range_bool
 \keys_define:nn { zref-clever/reference }
   {
@@ -1550,6 +2127,22 @@
           { \msg_warning:nn { zref-clever } { titleref-preamble-only } }
       }
   }
+\keys_define:nn { zref-clever/reference }
+  {
+    vario .code:n = { \RequirePackage { zref-vario } } ,
+    vario .value_forbidden:n = true ,
+  }
+\AddToHook { begindocument }
+  {
+    \keys_define:nn { zref-clever/reference }
+      {
+        vario .code:n =
+          {
+            \msg_warning:nnn { zref-clever }
+              { option-preamble-only } { vario }
+          }
+      }
+  }
 \tl_new:N \l__zrefclever_zcref_note_tl
 \keys_define:nn { zref-clever/reference }
   {
@@ -1848,6 +2441,7 @@
   {
     \tl_set:Nn \l__zrefclever_setup_type_tl {#1}
     \keys_set:nn { zref-clever/typesetup } {#2}
+    \tl_clear:N \l__zrefclever_setup_type_tl
   }
 \seq_map_inline:Nn
   \c__zrefclever_rf_opts_tl_not_type_specific_seq
@@ -1890,6 +2484,118 @@
   }
 \keys_define:nn { zref-clever/typesetup }
   {
+    endrange .code:n =
+      {
+        \str_case:nnF {#1}
+          {
+            { ref }
+            {
+              \tl_clear:c
+                {
+                  \__zrefclever_opt_varname_type:enn
+                    { \l__zrefclever_setup_type_tl } { endrangefunc } { tl }
+                }
+              \tl_clear:c
+                {
+                  \__zrefclever_opt_varname_type:enn
+                    { \l__zrefclever_setup_type_tl } { endrangeprop } { tl }
+                }
+            }
+
+            { stripprefix }
+            {
+              \tl_set:cn
+                {
+                  \__zrefclever_opt_varname_type:enn
+                    { \l__zrefclever_setup_type_tl } { endrangefunc } { tl }
+                }
+                { __zrefclever_get_endrange_stripprefix }
+              \tl_clear:c
+                {
+                  \__zrefclever_opt_varname_type:enn
+                    { \l__zrefclever_setup_type_tl } { endrangeprop } { tl }
+                }
+            }
+
+            { pagecomp }
+            {
+              \tl_set:cn
+                {
+                  \__zrefclever_opt_varname_type:enn
+                    { \l__zrefclever_setup_type_tl } { endrangefunc } { tl }
+                }
+                { __zrefclever_get_endrange_pagecomp }
+              \tl_clear:c
+                {
+                  \__zrefclever_opt_varname_type:enn
+                    { \l__zrefclever_setup_type_tl } { endrangeprop } { tl }
+                }
+            }
+
+            { pagecomp2 }
+            {
+              \tl_set:cn
+                {
+                  \__zrefclever_opt_varname_type:enn
+                    { \l__zrefclever_setup_type_tl } { endrangefunc } { tl }
+                }
+                { __zrefclever_get_endrange_pagecomptwo }
+              \tl_clear:c
+                {
+                  \__zrefclever_opt_varname_type:enn
+                    { \l__zrefclever_setup_type_tl } { endrangeprop } { tl }
+                }
+            }
+
+            { unset }
+            {
+              \__zrefclever_opt_tl_unset:c
+                {
+                  \__zrefclever_opt_varname_type:enn
+                    { \l__zrefclever_setup_type_tl } { endrangefunc } { tl }
+                }
+              \__zrefclever_opt_tl_unset:c
+                {
+                  \__zrefclever_opt_varname_type:enn
+                    { \l__zrefclever_setup_type_tl } { endrangeprop } { tl }
+                }
+            }
+          }
+          {
+            \tl_if_empty:nTF {#1}
+              {
+                \msg_warning:nnn { zref-clever }
+                  { endrange-property-undefined } {#1}
+              }
+              {
+                \zref at ifpropundefined {#1}
+                  {
+                    \msg_warning:nnn { zref-clever }
+                      { endrange-property-undefined } {#1}
+                  }
+                  {
+                    \tl_set:cn
+                      {
+                        \__zrefclever_opt_varname_type:enn
+                          { \l__zrefclever_setup_type_tl }
+                          { endrangefunc } { tl }
+                      }
+                      { __zrefclever_get_endrange_property }
+                    \tl_set:cn
+                      {
+                        \__zrefclever_opt_varname_type:enn
+                          { \l__zrefclever_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.
@@ -2195,6 +2901,247 @@
   }
 \keys_define:nn { zref-clever/langsetup }
   {
+    endrange .code:n =
+      {
+        \str_case:nnF {#1}
+          {
+            { ref }
+            {
+              \tl_if_empty:NTF \l__zrefclever_setup_type_tl
+                {
+                  \tl_gclear:c
+                    {
+                      \__zrefclever_opt_varname_lang_default:enn
+                        { \l__zrefclever_setup_language_tl }
+                        { endrangefunc } { tl }
+                    }
+                  \tl_gclear:c
+                    {
+                      \__zrefclever_opt_varname_lang_default:enn
+                        { \l__zrefclever_setup_language_tl }
+                        { endrangeprop } { tl }
+                    }
+                }
+                {
+                  \tl_gclear:c
+                    {
+                      \__zrefclever_opt_varname_lang_type:eenn
+                        { \l__zrefclever_setup_language_tl }
+                        { \l__zrefclever_setup_type_tl }
+                        { endrangefunc } { tl }
+                    }
+                  \tl_gclear:c
+                    {
+                      \__zrefclever_opt_varname_lang_type:eenn
+                        { \l__zrefclever_setup_language_tl }
+                        { \l__zrefclever_setup_type_tl }
+                        { endrangeprop } { tl }
+                    }
+                }
+            }
+
+            { stripprefix }
+            {
+              \tl_if_empty:NTF \l__zrefclever_setup_type_tl
+                {
+                  \tl_gset:cn
+                    {
+                      \__zrefclever_opt_varname_lang_default:enn
+                        { \l__zrefclever_setup_language_tl }
+                        { endrangefunc } { tl }
+                    }
+                    { __zrefclever_get_endrange_stripprefix }
+                  \tl_gclear:c
+                    {
+                      \__zrefclever_opt_varname_lang_default:enn
+                        { \l__zrefclever_setup_language_tl }
+                        { endrangeprop } { tl }
+                    }
+                }
+                {
+                  \tl_gset:cn
+                    {
+                      \__zrefclever_opt_varname_lang_type:eenn
+                        { \l__zrefclever_setup_language_tl }
+                        { \l__zrefclever_setup_type_tl }
+                        { endrangefunc } { tl }
+                    }
+                    { __zrefclever_get_endrange_stripprefix }
+                  \tl_gclear:c
+                    {
+                      \__zrefclever_opt_varname_lang_type:eenn
+                        { \l__zrefclever_setup_language_tl }
+                        { \l__zrefclever_setup_type_tl }
+                        { endrangeprop } { tl }
+                    }
+                }
+            }
+
+            { pagecomp }
+            {
+              \tl_if_empty:NTF \l__zrefclever_setup_type_tl
+                {
+                  \tl_gset:cn
+                    {
+                      \__zrefclever_opt_varname_lang_default:enn
+                        { \l__zrefclever_setup_language_tl }
+                        { endrangefunc } { tl }
+                    }
+                    { __zrefclever_get_endrange_pagecomp }
+                  \tl_gclear:c
+                    {
+                      \__zrefclever_opt_varname_lang_default:enn
+                        { \l__zrefclever_setup_language_tl }
+                        { endrangeprop } { tl }
+                    }
+                }
+                {
+                  \tl_gset:cn
+                    {
+                      \__zrefclever_opt_varname_lang_type:eenn
+                        { \l__zrefclever_setup_language_tl }
+                        { \l__zrefclever_setup_type_tl }
+                        { endrangefunc } { tl }
+                    }
+                    { __zrefclever_get_endrange_pagecomp }
+                  \tl_gclear:c
+                    {
+                      \__zrefclever_opt_varname_lang_type:eenn
+                        { \l__zrefclever_setup_language_tl }
+                        { \l__zrefclever_setup_type_tl }
+                        { endrangeprop } { tl }
+                    }
+                }
+            }
+
+            { pagecomp2 }
+            {
+              \tl_if_empty:NTF \l__zrefclever_setup_type_tl
+                {
+                  \tl_gset:cn
+                    {
+                      \__zrefclever_opt_varname_lang_default:enn
+                        { \l__zrefclever_setup_language_tl }
+                        { endrangefunc } { tl }
+                    }
+                    { __zrefclever_get_endrange_pagecomptwo }
+                  \tl_gclear:c
+                    {
+                      \__zrefclever_opt_varname_lang_default:enn
+                        { \l__zrefclever_setup_language_tl }
+                        { endrangeprop } { tl }
+                    }
+                }
+                {
+                  \tl_gset:cn
+                    {
+                      \__zrefclever_opt_varname_lang_type:eenn
+                        { \l__zrefclever_setup_language_tl }
+                        { \l__zrefclever_setup_type_tl }
+                        { endrangefunc } { tl }
+                    }
+                    { __zrefclever_get_endrange_pagecomptwo }
+                  \tl_gclear:c
+                    {
+                      \__zrefclever_opt_varname_lang_type:eenn
+                        { \l__zrefclever_setup_language_tl }
+                        { \l__zrefclever_setup_type_tl }
+                        { endrangeprop } { tl }
+                    }
+                }
+            }
+
+            { unset }
+            {
+              \tl_if_empty:NTF \l__zrefclever_setup_type_tl
+                {
+                  \__zrefclever_opt_tl_gunset:c
+                    {
+                      \__zrefclever_opt_varname_lang_default:enn
+                        { \l__zrefclever_setup_language_tl }
+                        { endrangefunc } { tl }
+                    }
+                  \__zrefclever_opt_tl_gunset:c
+                    {
+                      \__zrefclever_opt_varname_lang_default:enn
+                        { \l__zrefclever_setup_language_tl }
+                        { endrangeprop } { tl }
+                    }
+                }
+                {
+                  \__zrefclever_opt_tl_gunset:c
+                    {
+                      \__zrefclever_opt_varname_lang_type:eenn
+                        { \l__zrefclever_setup_language_tl }
+                        { \l__zrefclever_setup_type_tl }
+                        { endrangefunc } { tl }
+                    }
+                  \__zrefclever_opt_tl_gunset:c
+                    {
+                      \__zrefclever_opt_varname_lang_type:eenn
+                        { \l__zrefclever_setup_language_tl }
+                        { \l__zrefclever_setup_type_tl }
+                        { endrangeprop } { tl }
+                    }
+                }
+            }
+          }
+          {
+            \tl_if_empty:nTF {#1}
+              {
+                \msg_warning:nnn { zref-clever }
+                  { endrange-property-undefined } {#1}
+              }
+              {
+                \zref at ifpropundefined {#1}
+                  {
+                    \msg_warning:nnn { zref-clever }
+                      { endrange-property-undefined } {#1}
+                  }
+                  {
+                    \tl_if_empty:NTF \l__zrefclever_setup_type_tl
+                      {
+                        \tl_gset:cn
+                          {
+                            \__zrefclever_opt_varname_lang_default:enn
+                              { \l__zrefclever_setup_language_tl }
+                              { endrangefunc } { tl }
+                          }
+                          { __zrefclever_get_endrange_property }
+                        \tl_gset:cn
+                          {
+                            \__zrefclever_opt_varname_lang_default:enn
+                              { \l__zrefclever_setup_language_tl }
+                              { endrangeprop } { tl }
+                          }
+                          {#1}
+                      }
+                      {
+                        \tl_gset:cn
+                          {
+                            \__zrefclever_opt_varname_lang_type:eenn
+                              { \l__zrefclever_setup_language_tl }
+                              { \l__zrefclever_setup_type_tl }
+                              { endrangefunc } { tl }
+                          }
+                          { __zrefclever_get_endrange_property }
+                        \tl_gset:cn
+                          {
+                            \__zrefclever_opt_varname_lang_type:eenn
+                              { \l__zrefclever_setup_language_tl }
+                              { \l__zrefclever_setup_type_tl }
+                              { endrangeprop } { tl }
+                          }
+                          {#1}
+                      }
+                  }
+              }
+          }
+      } ,
+    endrange .value_required:n = true ,
+  }
+\keys_define:nn { zref-clever/langsetup }
+  {
     refpre .code:n =
       {
         % NOTE Option deprecated in 2022-01-10 for v0.1.2-alpha.
@@ -2578,7 +3525,7 @@
     \bool_set_false:N \l__zrefclever_sort_decided_bool
 
     % First we check if there's any "external document" difference (coming
-    % from 'zref-xr') and, if so, sort based on that.
+    % from `zref-xr') and, if so, sort based on that.
     \tl_if_eq:NNF
       \l__zrefclever_label_extdoc_a_tl
       \l__zrefclever_label_extdoc_b_tl
@@ -2774,6 +3721,8 @@
 \int_new:N \l__zrefclever_range_count_int
 \int_new:N \l__zrefclever_range_same_count_int
 \tl_new:N \l__zrefclever_range_beg_label_tl
+\bool_new:N \l__zrefclever_range_beg_is_first_bool
+\tl_new:N \l__zrefclever_range_end_ref_tl
 \bool_new:N \l__zrefclever_next_maybe_range_bool
 \bool_new:N \l__zrefclever_next_is_same_bool
 \tl_new:N \l__zrefclever_tpairsep_tl
@@ -2786,8 +3735,11 @@
 \tl_new:N \l__zrefclever_rangesep_tl
 \tl_new:N \l__zrefclever_namefont_tl
 \tl_new:N \l__zrefclever_reffont_tl
+\tl_new:N \l__zrefclever_endrangefunc_tl
+\tl_new:N \l__zrefclever_endrangeprop_tl
 \bool_new:N \l__zrefclever_cap_bool
 \bool_new:N \l__zrefclever_abbrev_bool
+\bool_new:N \l__zrefclever_rangetopair_bool
 \seq_new:N \l__zrefclever_refbounds_first_seq
 \seq_new:N \l__zrefclever_refbounds_first_sg_seq
 \seq_new:N \l__zrefclever_refbounds_first_pb_seq
@@ -2810,11 +3762,13 @@
     \tl_clear:N \l__zrefclever_type_first_label_tl
     \tl_clear:N \l__zrefclever_type_first_label_type_tl
     \tl_clear:N \l__zrefclever_range_beg_label_tl
+    \tl_clear:N \l__zrefclever_range_end_ref_tl
     \int_zero:N \l__zrefclever_label_count_int
     \int_zero:N \l__zrefclever_type_count_int
     \int_zero:N \l__zrefclever_ref_count_int
     \int_zero:N \l__zrefclever_range_count_int
     \int_zero:N \l__zrefclever_range_same_count_int
+    \bool_set_false:N \l__zrefclever_range_beg_is_first_bool
     \bool_set_false:N \l__zrefclever_type_first_refbounds_set_bool
 
     % Get type block options (not type-specific).
@@ -2889,9 +3843,9 @@
           }
 
         % Handle warnings in case of reference or type undefined.
-        % Test: 'zc-typeset01.lvt': "Typeset refs: warn ref undefined"
+        % Test: `zc-typeset01.lvt': "Typeset refs: warn ref undefined"
         \zref at refused { \l__zrefclever_label_a_tl }
-        % Test: 'zc-typeset01.lvt': "Typeset refs: warn missing type"
+        % Test: `zc-typeset01.lvt': "Typeset refs: warn missing type"
         \zref at ifrefundefined { \l__zrefclever_label_a_tl }
           {}
           {
@@ -2943,6 +3897,14 @@
               { \l__zrefclever_label_type_a_tl }
               { \l__zrefclever_ref_language_tl }
               \l__zrefclever_reffont_tl
+            \__zrefclever_get_rf_opt_tl:nxxN { endrangefunc }
+              { \l__zrefclever_label_type_a_tl }
+              { \l__zrefclever_ref_language_tl }
+              \l__zrefclever_endrangefunc_tl
+            \__zrefclever_get_rf_opt_tl:nxxN { endrangeprop }
+              { \l__zrefclever_label_type_a_tl }
+              { \l__zrefclever_ref_language_tl }
+              \l__zrefclever_endrangeprop_tl
             \__zrefclever_get_rf_opt_bool:nnxxN { cap } { false }
               { \l__zrefclever_label_type_a_tl }
               { \l__zrefclever_ref_language_tl }
@@ -2951,6 +3913,10 @@
               { \l__zrefclever_label_type_a_tl }
               { \l__zrefclever_ref_language_tl }
               \l__zrefclever_abbrev_bool
+            \__zrefclever_get_rf_opt_bool:nnxxN { rangetopair } { true }
+              { \l__zrefclever_label_type_a_tl }
+              { \l__zrefclever_ref_language_tl }
+              \l__zrefclever_rangetopair_bool
             \__zrefclever_get_rf_opt_seq:nxxN { refbounds-first }
               { \l__zrefclever_label_type_a_tl }
               { \l__zrefclever_ref_language_tl }
@@ -3008,7 +3974,7 @@
       {
         % 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"
+        % Test: `zc-typeset01.lvt': "Last of type: single"
         { 0 }
         {
           \tl_set:NV \l__zrefclever_type_first_label_tl
@@ -3021,7 +3987,7 @@
         }
 
         % The last is the second: we have a pair (if not repeated).
-        % Test: 'zc-typeset01.lvt': "Last of type: pair"
+        % Test: `zc-typeset01.lvt': "Last of type: pair"
         { 1 }
         {
           \int_compare:nNnTF { \l__zrefclever_range_same_count_int } = { 1 }
@@ -3049,7 +4015,7 @@
         \int_case:nnF { \l__zrefclever_range_count_int }
           {
             % There was no range going on.
-            % Test: 'zc-typeset01.lvt': "Last of type: not range"
+            % Test: `zc-typeset01.lvt': "Last of type: not range"
             { 0 }
             {
               \int_compare:nNnTF { \l__zrefclever_ref_count_int } < { 2 }
@@ -3071,15 +4037,15 @@
                 }
             }
             % Last in the range is also the second in it.
-            % Test: 'zc-typeset01.lvt': "Last of type: pair in sequence"
+            % Test: `zc-typeset01.lvt': "Last of type: pair in sequence"
             { 1 }
             {
               \int_compare:nNnTF
                 { \l__zrefclever_range_same_count_int } = { 1 }
                 {
-                  % We know `range_beg_label' is not empty, since this is the
-                  % second element in the range, but the third or more in the
-                  % type list.
+                  % 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__zrefclever_typeset_queue_curr_tl
                     {
                       \exp_not:V \l__zrefclever_pairsep_tl
@@ -3115,13 +4081,13 @@
               }
               {
                 % Repetition, not a range.
-                % Test: 'zc-typeset01.lvt': "Last of type: range to one"
+                % Test: `zc-typeset01.lvt': "Last of type: range to one"
                 { 0 }
                 {
-                  % If `range_beg_label' is empty, it means it was also the
-                  % first of the type, and hence its typesetting was already
-                  % handled, and we just have to set refbounds.
-                  \tl_if_empty:VTF \l__zrefclever_range_beg_label_tl
+                  % 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__zrefclever_range_beg_is_first_bool
                     {
                       \seq_set_eq:NN \l__zrefclever_type_first_refbounds_seq
                         \l__zrefclever_refbounds_first_sg_seq
@@ -3153,11 +4119,11 @@
                 }
                 % 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"
+                % Test: `zc-typeset01.lvt': "Last of type: range to pair"
                 { 1 }
                 {
                   % Ditto.
-                  \tl_if_empty:VTF \l__zrefclever_range_beg_label_tl
+                  \bool_if:NTF \l__zrefclever_range_beg_is_first_bool
                     {
                       \seq_set_eq:NN \l__zrefclever_type_first_refbounds_seq
                         \l__zrefclever_refbounds_first_pb_seq
@@ -3189,9 +4155,9 @@
               }
               {
                 % An actual range.
-                % Test: 'zc-typeset01.lvt': "Last of type: range"
+                % Test: `zc-typeset01.lvt': "Last of type: range"
                 % Ditto.
-                \tl_if_empty:VTF \l__zrefclever_range_beg_label_tl
+                \bool_if:NTF \l__zrefclever_range_beg_is_first_bool
                   {
                     \seq_set_eq:NN \l__zrefclever_type_first_refbounds_seq
                       \l__zrefclever_refbounds_first_rb_seq
@@ -3225,12 +4191,31 @@
                           }
                       }
                   }
-                \tl_put_right:Nx \l__zrefclever_typeset_queue_curr_tl
+                \bool_lazy_and:nnTF
+                  { ! \tl_if_empty_p:N \l__zrefclever_endrangefunc_tl }
+                  { \cs_if_exist_p:c { \l__zrefclever_endrangefunc_tl :VVN } }
                   {
-                    \exp_not:V \l__zrefclever_rangesep_tl
-                    \__zrefclever_get_ref:VN \l__zrefclever_label_a_tl
-                      \l__zrefclever_refbounds_last_re_seq
+                    \use:c { \l__zrefclever_endrangefunc_tl :VVN }
+                      \l__zrefclever_range_beg_label_tl
+                      \l__zrefclever_label_a_tl
+                      \l__zrefclever_range_end_ref_tl
+                    \tl_put_right:Nx \l__zrefclever_typeset_queue_curr_tl
+                      {
+                        \exp_not:V \l__zrefclever_rangesep_tl
+                        \__zrefclever_get_ref_endrange:VVN
+                          \l__zrefclever_label_a_tl
+                          \l__zrefclever_range_end_ref_tl
+                          \l__zrefclever_refbounds_last_re_seq
+                      }
                   }
+                  {
+                    \tl_put_right:Nx \l__zrefclever_typeset_queue_curr_tl
+                      {
+                        \exp_not:V \l__zrefclever_rangesep_tl
+                        \__zrefclever_get_ref:VN \l__zrefclever_label_a_tl
+                          \l__zrefclever_refbounds_last_re_seq
+                      }
+                  }
               }
           }
       }
@@ -3252,15 +4237,18 @@
           }
           {
             \bool_set_false:N \l__zrefclever_next_maybe_range_bool
-            \zref at ifrefundefined { \l__zrefclever_type_first_label_tl }
-              { }
+            \bool_if:NT \l__zrefclever_rangetopair_bool
               {
-                \__zrefclever_labels_in_sequence:nn
-                  { \l__zrefclever_type_first_label_tl }
-                  { \l__zrefclever_label_a_tl }
+                \zref at ifrefundefined { \l__zrefclever_type_first_label_tl }
+                  { }
+                  {
+                    \__zrefclever_labels_in_sequence:nn
+                      { \l__zrefclever_type_first_label_tl }
+                      { \l__zrefclever_label_a_tl }
+                  }
               }
-            % Test: 'zc-typeset01.lvt': "Last of type: option range"
-            % Test: 'zc-typeset01.lvt': "Last of type: option range to pair"
+            % Test: `zc-typeset01.lvt': "Last of type: option range"
+            % Test: `zc-typeset01.lvt': "Last of type: option range to pair"
             \bool_if:NTF \l__zrefclever_next_maybe_range_bool
               {
                 \tl_set:Nx \l__zrefclever_typeset_queue_curr_tl
@@ -3274,12 +4262,35 @@
                 \bool_set_true:N \l__zrefclever_type_first_refbounds_set_bool
               }
               {
-                \tl_set:Nx \l__zrefclever_typeset_queue_curr_tl
+                \bool_lazy_and:nnTF
+                  { ! \tl_if_empty_p:N \l__zrefclever_endrangefunc_tl }
+                  { \cs_if_exist_p:c { \l__zrefclever_endrangefunc_tl :VVN } }
                   {
-                    \exp_not:V \l__zrefclever_rangesep_tl
-                    \__zrefclever_get_ref:VN \l__zrefclever_label_a_tl
-                      \l__zrefclever_refbounds_last_re_seq
+                    % 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__zrefclever_endrangefunc_tl :VVN }
+                      \l__zrefclever_type_first_label_tl
+                      \l__zrefclever_label_a_tl
+                      \l__zrefclever_range_end_ref_tl
+                    \tl_set:Nx \l__zrefclever_typeset_queue_curr_tl
+                      {
+                        \exp_not:V \l__zrefclever_rangesep_tl
+                        \__zrefclever_get_ref_endrange:VVN
+                          \l__zrefclever_label_a_tl
+                          \l__zrefclever_range_end_ref_tl
+                          \l__zrefclever_refbounds_last_re_seq
+                      }
                   }
+                  {
+                    \tl_set:Nx \l__zrefclever_typeset_queue_curr_tl
+                      {
+                        \exp_not:V \l__zrefclever_rangesep_tl
+                        \__zrefclever_get_ref:VN \l__zrefclever_label_a_tl
+                          \l__zrefclever_refbounds_last_re_seq
+                      }
+                  }
                 \seq_set_eq:NN \l__zrefclever_type_first_refbounds_seq
                   \l__zrefclever_refbounds_first_rb_seq
                 \bool_set_true:N \l__zrefclever_type_first_refbounds_set_bool
@@ -3308,7 +4319,7 @@
       {
         \bool_if:NTF \l__zrefclever_typeset_ref_bool
           {
-            % Test: 'zc-typeset01.lvt': "Last of type: option typeset ref"
+            % Test: `zc-typeset01.lvt': "Last of type: option typeset ref"
             \tl_put_left:Nx \l__zrefclever_typeset_queue_curr_tl
               {
                 \__zrefclever_get_ref:VN \l__zrefclever_type_first_label_tl
@@ -3318,7 +4329,7 @@
           {
             \bool_if:NTF \l__zrefclever_typeset_name_bool
               {
-                % Test: 'zc-typeset01.lvt': "Last of type: option typeset name"
+                % Test: `zc-typeset01.lvt': "Last of type: option typeset name"
                 \tl_set:Nx \l__zrefclever_typeset_queue_curr_tl
                   {
                     \bool_if:NTF \l__zrefclever_name_in_link_bool
@@ -3325,7 +4336,7 @@
                       {
                         \exp_not:N \group_begin:
                         \exp_not:V \l__zrefclever_namefont_tl
-                        % It's two '@s', but escaped for DocStrip.
+                        % It's two `@s', but escaped for DocStrip.
                         \exp_not:N \hyper@@link
                           {
                             \__zrefclever_extract_url_unexp:V
@@ -3352,7 +4363,7 @@
                 % 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"
+                % Test: `zc-typeset01.lvt': "Last of type: option typeset none"
                 \tl_put_left:Nx \l__zrefclever_typeset_queue_curr_tl
                   { \__zrefclever_get_ref_first: }
               }
@@ -3378,11 +4389,11 @@
         \int_case:nnF { \l__zrefclever_type_count_int }
           {
             % Single type.
-            % Test: 'zc-typeset01.lvt': "Last of type: single type"
+            % Test: `zc-typeset01.lvt': "Last of type: single type"
             { 0 }
             { \l__zrefclever_typeset_queue_curr_tl }
             % Pair of types.
-            % Test: 'zc-typeset01.lvt': "Last of type: pair of types"
+            % Test: `zc-typeset01.lvt': "Last of type: pair of types"
             { 1 }
             {
               \l__zrefclever_tpairsep_tl
@@ -3391,7 +4402,7 @@
           }
           {
             % Last in list of types.
-            % Test: 'zc-typeset01.lvt': "Last of type: list of types"
+            % Test: `zc-typeset01.lvt': "Last of type: list of types"
             \l__zrefclever_tlastsep_tl
             \l__zrefclever_typeset_queue_curr_tl
           }
@@ -3412,11 +4423,13 @@
         \tl_clear:N \l__zrefclever_type_first_label_tl
         \tl_clear:N \l__zrefclever_type_first_label_type_tl
         \tl_clear:N \l__zrefclever_range_beg_label_tl
+        \tl_clear:N \l__zrefclever_range_end_ref_tl
         \int_zero:N \l__zrefclever_label_count_int
         \int_zero:N \l__zrefclever_ref_count_int
         \int_incr:N \l__zrefclever_type_count_int
         \int_zero:N \l__zrefclever_range_count_int
         \int_zero:N \l__zrefclever_range_same_count_int
+        \bool_set_false:N \l__zrefclever_range_beg_is_first_bool
         \bool_set_false:N \l__zrefclever_type_first_refbounds_set_bool
       }
   }
@@ -3447,13 +4460,16 @@
           \l__zrefclever_label_type_a_tl
         \int_incr:N \l__zrefclever_ref_count_int
 
-        % If the next label may be part of a range, we set `range_beg_label'
-        % to "empty" (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"
+        % 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__zrefclever_next_maybe_range_bool
           {
-            \tl_clear:N \l__zrefclever_range_beg_label_tl
+            \bool_set_true:N \l__zrefclever_range_beg_is_first_bool
+            \tl_set:NV \l__zrefclever_range_beg_label_tl
+              \l__zrefclever_label_a_tl
+            \tl_clear:N \l__zrefclever_range_end_ref_tl
             \int_incr:N \l__zrefclever_range_count_int
             \bool_if:NT \l__zrefclever_next_is_same_bool
               { \int_incr:N \l__zrefclever_range_same_count_int }
@@ -3470,6 +4486,7 @@
                 % There was no range going, we are starting one.
                 \tl_set:NV \l__zrefclever_range_beg_label_tl
                   \l__zrefclever_label_a_tl
+                \tl_clear:N \l__zrefclever_range_end_ref_tl
                 \int_incr:N \l__zrefclever_range_count_int
                 \bool_if:NT \l__zrefclever_next_is_same_bool
                   { \int_incr:N \l__zrefclever_range_same_count_int }
@@ -3487,7 +4504,7 @@
             \int_case:nnF { \l__zrefclever_range_count_int }
               {
                 % There was no range going on.
-                % Test: 'zc-typeset01.lvt': "Not last of type: no range"
+                % Test: `zc-typeset01.lvt': "Not last of type: no range"
                 { 0 }
                 {
                   \int_incr:N \l__zrefclever_ref_count_int
@@ -3501,11 +4518,11 @@
                 % 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"
+                % Test: `zc-typeset01.lvt': "Not last of type: range pair to one"
+                % Test: `zc-typeset01.lvt': "Not last of type: range pair"
                 { 1 }
                 {
-                  \tl_if_empty:VTF \l__zrefclever_range_beg_label_tl
+                  \bool_if:NTF \l__zrefclever_range_beg_is_first_bool
                     {
                       \seq_set_eq:NN \l__zrefclever_type_first_refbounds_seq
                         \l__zrefclever_refbounds_first_seq
@@ -3547,10 +4564,10 @@
                     \l__zrefclever_range_same_count_int
                   }
                   {
-                    % Test: 'zc-typeset01.lvt': "Not last of type: range to one"
+                    % Test: `zc-typeset01.lvt': "Not last of type: range to one"
                     { 0 }
                     {
-                      \tl_if_empty:VTF \l__zrefclever_range_beg_label_tl
+                      \bool_if:NTF \l__zrefclever_range_beg_is_first_bool
                         {
                           \seq_set_eq:NN
                             \l__zrefclever_type_first_refbounds_seq
@@ -3569,10 +4586,10 @@
                             }
                         }
                     }
-                    % Test: 'zc-typeset01.lvt': "Not last of type: range to pair"
+                    % Test: `zc-typeset01.lvt': "Not last of type: range to pair"
                     { 1 }
                     {
-                      \tl_if_empty:VTF \l__zrefclever_range_beg_label_tl
+                      \bool_if:NTF \l__zrefclever_range_beg_is_first_bool
                         {
                           \seq_set_eq:NN
                             \l__zrefclever_type_first_refbounds_seq
@@ -3600,8 +4617,8 @@
                     }
                   }
                   {
-                    % Test: 'zc-typeset01.lvt': "Not last of type: range"
-                    \tl_if_empty:VTF \l__zrefclever_range_beg_label_tl
+                    % Test: `zc-typeset01.lvt': "Not last of type: range"
+                    \bool_if:NTF \l__zrefclever_range_beg_is_first_bool
                       {
                         \seq_set_eq:NN
                           \l__zrefclever_type_first_refbounds_seq
@@ -3620,16 +4637,35 @@
                           }
                       }
                     % 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
+                    % 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'.
-                    \tl_put_right:Nx \l__zrefclever_typeset_queue_curr_tl
+                    % incrementing `ref_count_int'.
+                    \bool_lazy_and:nnTF
+                      { ! \tl_if_empty_p:N \l__zrefclever_endrangefunc_tl }
+                      { \cs_if_exist_p:c { \l__zrefclever_endrangefunc_tl :VVN } }
                       {
-                        \exp_not:V \l__zrefclever_rangesep_tl
-                        \__zrefclever_get_ref:VN \l__zrefclever_label_a_tl
-                          \l__zrefclever_refbounds_mid_re_seq
+                        \use:c { \l__zrefclever_endrangefunc_tl :VVN }
+                          \l__zrefclever_range_beg_label_tl
+                          \l__zrefclever_label_a_tl
+                          \l__zrefclever_range_end_ref_tl
+                        \tl_put_right:Nx \l__zrefclever_typeset_queue_curr_tl
+                          {
+                            \exp_not:V \l__zrefclever_rangesep_tl
+                            \__zrefclever_get_ref_endrange:VVN
+                              \l__zrefclever_label_a_tl
+                              \l__zrefclever_range_end_ref_tl
+                              \l__zrefclever_refbounds_mid_re_seq
+                          }
                       }
+                      {
+                        \tl_put_right:Nx \l__zrefclever_typeset_queue_curr_tl
+                          {
+                            \exp_not:V \l__zrefclever_rangesep_tl
+                            \__zrefclever_get_ref:VN \l__zrefclever_label_a_tl
+                              \l__zrefclever_refbounds_mid_re_seq
+                          }
+                      }
                   }
               }
             % Reset counters.
@@ -3685,6 +4721,45 @@
       { \__zrefclever_ref_default: }
   }
 \cs_generate_variant:Nn \__zrefclever_get_ref:nN { VN }
+\cs_new:Npn \__zrefclever_get_ref_endrange:nnN #1#2#3
+  {
+    \str_if_eq:nnTF {#2} { zc at missingproperty }
+      { \__zrefclever_ref_default: }
+      {
+        \bool_if:nTF
+          {
+            \l__zrefclever_hyperlink_bool &&
+            ! \l__zrefclever_link_star_bool
+          }
+          {
+            \exp_not:N \group_begin:
+            \exp_not:V \l__zrefclever_reffont_tl
+            \seq_item:Nn #3 { 1 }
+            % It's two `@s', but escaped for DocStrip.
+            \exp_not:N \hyper@@link
+              { \__zrefclever_extract_url_unexp:n {#1} }
+              { \__zrefclever_extract_unexp:nnn {#1} { anchor } { } }
+              {
+                \seq_item:Nn #3 { 2 }
+                \exp_not:n {#2}
+                \seq_item:Nn #3 { 3 }
+              }
+            \seq_item:Nn #3 { 4 }
+            \exp_not:N \group_end:
+          }
+          {
+            \exp_not:N \group_begin:
+            \exp_not:V \l__zrefclever_reffont_tl
+            \seq_item:Nn #3 { 1 }
+            \seq_item:Nn #3 { 2 }
+            \exp_not:n {#2}
+            \seq_item:Nn #3 { 3 }
+            \seq_item:Nn #3 { 4 }
+            \exp_not:N \group_end:
+          }
+      }
+  }
+\cs_generate_variant:Nn \__zrefclever_get_ref_endrange:nnN { VVN }
 \cs_new:Npn \__zrefclever_get_ref_first:
   {
     \zref at ifrefundefined { \l__zrefclever_type_first_label_tl }
@@ -3764,7 +4839,7 @@
                     \exp_not:V \l__zrefclever_reffont_tl
                     \seq_item:Nn
                       \l__zrefclever_type_first_refbounds_seq { 1 }
-                    % It's two '@s', but escaped for DocStrip.
+                    % It's two `@s', but escaped for DocStrip.
                     \exp_not:N \hyper@@link
                       {
                         \__zrefclever_extract_url_unexp:V
@@ -4052,14 +5127,9 @@
 \cs_generate_variant:Nn \__zrefclever_extract_url_unexp:n { V }
 \cs_new_protected:Npn \__zrefclever_labels_in_sequence:nn #1#2
   {
-    \__zrefclever_extract_default:Nnnn \l__zrefclever_label_extdoc_a_tl
-      {#1} { externaldocument } { }
-    \__zrefclever_extract_default:Nnnn \l__zrefclever_label_extdoc_b_tl
-      {#2} { externaldocument } { }
-
-    \tl_if_eq:NNT
-      \l__zrefclever_label_extdoc_a_tl
-      \l__zrefclever_label_extdoc_b_tl
+    \exp_args:Nxx \tl_if_eq:nnT
+      { \__zrefclever_extract_unexp:nnn {#1} { externaldocument } { } }
+      { \__zrefclever_extract_unexp:nnn {#2} { externaldocument } { } }
       {
         \tl_if_eq:NnTF \l__zrefclever_ref_property_tl { page }
           {
@@ -4104,8 +5174,6 @@
                             =
                           { \__zrefclever_extract:nnn {#2} { zc at cntval } { -1 } }
                           {
-                            \bool_set_true:N
-                              \l__zrefclever_next_maybe_range_bool
                             \exp_args:Nxx \tl_if_eq:nnT
                               {
                                 \__zrefclever_extract_unexp:nvn {#1}
@@ -4117,6 +5185,8 @@
                               }
                               {
                                 \bool_set_true:N
+                                  \l__zrefclever_next_maybe_range_bool
+                                \bool_set_true:N
                                   \l__zrefclever_next_is_same_bool
                               }
                           }
@@ -4424,6 +5494,7 @@
             \cs_new_eq:NN \__zrefclever_orig_ltxlabel:n \ltx at label
             \cs_set_eq:NN \ltx at label \__zrefclever_ltxlabel:n
           }
+        \bool_new:N \l__zrefclever_amsmath_subequations_bool
         \AddToHook { env / subequations / begin }
           {
             \__zrefclever_zcsetup:x
@@ -4437,7 +5508,9 @@
                 currentcounter = parentequation ,
                 countertype = { parentequation = equation } ,
               }
+            \bool_set_true:N \l__zrefclever_amsmath_subequations_bool
           }
+        \zref at newprop { subeq } { \alph { equation } }
         \clist_map_inline:nn
           {
             equation ,
@@ -4457,7 +5530,11 @@
           }
           {
             \AddToHook { env / #1 / begin }
-              { \__zrefclever_zcsetup:n { currentcounter = equation } }
+              {
+                \__zrefclever_zcsetup:n { currentcounter = equation }
+                \bool_if:NT \l__zrefclever_amsmath_subequations_bool
+                  { \zref at localaddprop \ZREF at mainlist { subeq } }
+              }
           }
         \zcRefTypeSetup { equation }
           { reffont = \upshape }
@@ -4504,6 +5581,7 @@
   {
     \__zrefclever_if_package_loaded:nT { breqn }
       {
+        \bool_new:N \l__zrefclever_breqn_dgroup_bool
         \AddToHook { env / dgroup / begin }
           {
             \__zrefclever_zcsetup:x
@@ -4517,7 +5595,11 @@
                 currentcounter = parentequation ,
                 countertype = { parentequation = equation } ,
               }
+            \bool_set_true:N \l__zrefclever_breqn_dgroup_bool
           }
+        \zref at ifpropundefined { subeq }
+          { \zref at newprop { subeq } { \alph { equation } } }
+          { }
         \clist_map_inline:nn
           {
             dmath ,
@@ -4526,7 +5608,11 @@
           }
           {
             \AddToHook { env / #1 / begin }
-              { \__zrefclever_zcsetup:n { currentcounter = equation } }
+              {
+                \__zrefclever_zcsetup:n { currentcounter = equation }
+                \bool_if:NT \l__zrefclever_breqn_dgroup_bool
+                  { \zref at localaddprop \ZREF at mainlist { subeq } }
+              }
           }
         \msg_info:nnn { zref-clever } { compat-package } { breqn }
       }



More information about the tex-live-commits mailing list.