2

I am currently trying to create new commands to add the correct French article in front of the cross-references when required (for example "la figure XX" or "le chapitre XX").

My initial idea was to create commands like \acref and \Acref somehow like that:

\newcommand{\acref}[1]{%
\def\rtype{\namecref{#1}}% namecref will produce something like "figure"
\def\article{{\bf error:[\rtype](#1)} }% default value to help debugging
\ifstrequal{\rtype}{figure}{\def\article{la }}{}%
\article\cref{#1}\xspace
}

I also tried adding \expandafter around \ifstrequal like that:

\expandafter\ifstrequal\expandafter{\rtype}{figure}{\def\article{la }}{}%

And I tried some other combinations of commands that I do not master, with bad results.

All my attempts produced an invalid output, either always using the article for figure (as it is currently the only one I try to test) because both branches of the conditional statement are executed (with too much \expandafter) or it never enters the true branch.

Minimal compiling and not working example:

\documentclass{paper}

\usepackage{cleveref}
\usepackage{etoolbox}
\usepackage{xspace}

\crefname{figure}{figure}{figures} % instead of fig.

\newcommand{\acref}[1]{%
\def\rtype{\namecref{#1}}% namecref will produce something like "figure"
\def\article{{\bf error:[\rtype](#1)} }% default value to help debugging
\ifstrequal{\rtype}{figure}{\def\article{la }}{}%
\article\cref{#1}\xspace
}

\begin{document}
  \section{Title}
  \label{sec:title}

  \begin{figure}
    \caption{Caption}
    \label{fig:caption}
  \end{figure}

  Test with figure: \acref{fig:caption}. % should print "la figure" but prints error:...

  Test with section: \acref{sec:title}. % should print error:...
\end{document}

I read that etoolbox does not expand its arguments in most of its tests, including \ifstrequal. I had the idea to create a list containing the result of \namecref as there is \listeadd which expands its item argument and then test if that list contains the text "figure". However, when I tried (\listeadd{\mylist}{\namecref{#1}}), pdflatex stopped compiling and returned (filtered):

! Argument of \@firstoftwo has an extra }
! Paragraph ended before \@firstoftwo was complete.
! TeX capacity exceeded, sorry [parameter stack size=10000].

So, is \namecref unexpandable?


Really close questions that I failed to make work in my case:

Soko
  • 23
  • 2
  • 1
    Welcome to TeX.SE. Is there something stopping you from simply writing \crefname{figure}{la figure}{les figures}? – Mico Apr 17 '20 at 13:25
  • 1
    I agree with@Mico's proposal. In the rare cases where the article wouldn't be used in French, you always can type the name of the environment and use a plain \ref. – Bernard Apr 17 '20 at 13:32
  • (perhaps I should edit my question?) No, this is not a solution in my case: first, the article would be included in the link (when using hyperref and because I like to have "figure" included in the link); then, and mostly, because I often use \cref like some text (\cref{fig:lbl}) which would be incorrectly expanded as "some text (la figure 2)". @Bernard and, I find using \ref defeats one advantage of \cref that allows to change easily all prefixes for a given label type. – Soko Apr 17 '20 at 13:45
  • 1
    Happily you do not write in Latin or Greek (I suppose) – you'd have to provide for the different possibilities of declensions and cases (albeit one rarely uses the vocative for a figure or a table, as far as I know). – Bernard Apr 17 '20 at 14:58
  • You might be interested in xcref. It's a proof of concept but works (see the examples in French and German). Some names had to be changed in the latest commit, which haven't propagated to the manual yet, but this only concerns one particular feature. – frougon Apr 17 '20 at 19:31
  • @frougon Indeed it seems interesting, however I cannot make it to compile correctly. The first execution of pdflatex produces a PDF with ??. The second one throws the error Undefined control sequence. <recently read> \text_titlecase:nn on line 109 of the french example (... via \textsf{xcref} \fxcref{sec-exemples}. – Soko Apr 20 '20 at 09:29
  • Your expl3 is too old . \text_titlecase:nn was introduced in LaTeX2e <2020-02-02> patch level 1 (previously, there were other functions for the same thing in expl3, but they behaved differently; this is the name changing I mentioned above). Besides, requiring at least two compilations when using references is quite normal—this is how references work and explains the ?? after only one compilation run. – frougon Apr 20 '20 at 09:43
  • If it's too difficult to upgrade expl3, xcref commit 68342b43f24 should work for you, but beware that it will break when your LaTeX becomes up-to-date. – frougon Apr 20 '20 at 09:57
  • It works, however it produces links including the prefix with [nameinlink] from cleveref. I tried to read your source, but it is far above my current level. Do you locally modify inner cleveref's variables or do you produce yourself the text of the cross-reference? If the latter is true, how do you test the label type? – Soko Apr 20 '20 at 12:41
  • You should use @frougon, otherwise I'm not notified. The nameinlink thing is one of the biggest things I'm unhappy about, but I believe it can't be fixed using my approach without hacking cleveref (the other being that it is a brute force approach). The answer to your question is “the former”: when they know the circumstances (combination of form and preposition for French, but for German, the case also matters), my commands call \crefname and \Crefname for all known ref types (this is where it is brute force), which locally changes the results produced by cleveref. – frougon Apr 20 '20 at 13:01
  • Look for \@@_call_crefname:nnn and \@@_call_Crefname:nnn in my code, maybe this will help you understand. I believe what you wanted to do can't be done this way because cleveref doesn't produce its results in an “expandable” manner (you can't get the result in \x using \edef\x{...}, where ... contains simple calls to cleveref macros). – frougon Apr 20 '20 at 13:02
  • You might be interested in this answer of mine too: it shows how you can retrieve the cleveref name (theorem, proposition, etc.) associated to a given ref, programmatically transform it depending on which one it is, and also retrieve the number/letter/etc. (what cleveref calls the “label”) associated to the ref. In fact, I think the first part is exactly what you need for this question. – frougon Apr 20 '20 at 13:12

1 Answers1

2

Welcome to TeX.SE. You can use \cref@gettype to get the type of a cleveref reference in a macro of your choice (\mycref@type in the code below), then execute special code depending on the result. \cref@gettype expects that the \r@〈reference〉@cref macro has already been defined, which is done when the \newlabel command present in the .aux file is executed after the first compilation run with the 〈reference〉 in question. Therefore, my code checks whether the \r@〈reference〉@cref macro is defined and if not, prints ?? in the document along with the usual message on the terminal and in the log file:

LaTeX Warning: Label(s) may have changed. Rerun to get cross-references right.

I removed the use of xspace in your code, because the original author of xspace nowadays advises against using it (moreover, as egreg pointed out, xspace wouldn't be useful here, since your macro takes an argument). I changed the fig:caption label to fig-caption, otherwise with cleveref and : as an active character,1 you'll encounter problems. Please also note that I use amsthm for the theorem in the example. Should you use theorem environments defined with \newtheorem from the LaTeX kernel, you'd find that the cleveref type for theorems using a shared counter is not necessarily what you expect (you'd get the name of the shared counter instead of the “specific” name; this doesn't happen with amsthm theorem environments).

Finally, don't forget to load cleveref last! :-)

\documentclass{article}
\usepackage{expl3}
\usepackage{amsthm}             % only for the demo
%\usepackage{hyperref}          % usually last, but before cleveref
\usepackage{cleveref}           % load this package last

\newtheorem{theorem}{Théorème}

\crefname{figure}{figure}{figures}    % instead of fig.
\crefname{theorem}{théorème}{théorèmes}

\makeatletter
\ExplSyntaxOn
% Let's borrow \str_case:onF from expl3 (\cs_new_eq:NN is like \let but checks
% that the “destination” command name doesn't already exist).
\cs_new_eq:NN \mycref@str@oswitch \str_case:onF
\ExplSyntaxOff

\newcommand*{\acref}[1]{%
  \ifcsname r@#1@cref\endcsname
    \cref@gettype{#1}{\mycref@type}%
    \mycref@str@oswitch{\mycref@type}
      {{figure}{la }%
       {theorem}{le }%
%      {...}{...}% you can add as many cases as you want!
      }
      {\errmessage{%
         My package: unknown reference type for label '#1':
         '\unexpanded\expandafter{\mycref@type}'}%
      }%
  \else
    \G@refundefinedtrue
    \nfss@text{\reset@font\bfseries ??}%
    \@latex@warning{Reference `#1' on page \thepage\space undefined}%
  \fi
  \cref{#1}%
}
\makeatother

\begin{document}

  \section{Title}\label{sec-title}

  \begin{theorem}\label{th-some-theorem}
    This is a theorem.
  \end{theorem}

  \begin{figure}
    \centering X
    \caption{Caption}
    \label{fig-caption}
  \end{figure}

  Test with figure: \acref{fig-caption}.

  Test with theorem: \acref{th-some-theorem}.

% Test with section: \acref{sec-title}. % prints an error, as expected

\end{document}

enter image description here

If you uncomment the \usepackage{hyperref} line and recompile until LaTeX is happy (because of the change in \newlabel syntax this implies), you'll get:

enter image description here


Footnote

  1. Which will normally be the case if you use \usepackage[french]{babel} with the pdfTeX engine.
frougon
  • 24,283
  • 1
  • 32
  • 55
  • By the way, \xspace is and has always been pretty useless in macros with arguments. – egreg Apr 20 '20 at 14:26
  • @egreg I believe the way the OP used xspace here was correct, wasn't it? Once the OP's \acref is expanded, \xspace would see a period, or a space, or... ? – frougon Apr 20 '20 at 14:31
  • @frougon ... well, I was reading and trying to adapt the link you gave to post something like you just did. I will accept the answer after some testing. I get ! Argument of \@fourthoffive has an extra }. if I use hyperref, however the second compilation works perfectly and produces the correct result. What I was trying to do produced the same but with \@firstoftwo and I was trying to get rid of that. Perhaps it is normal for first compilation? As for cleveref with babel, I did something probably ugly but working, I renewed to encapsulate inside otherlanguage to locally disable :. – Soko Apr 20 '20 at 14:43
  • hyperref and standard LaTeX use different syntaxes for \newlabel (and cleveref also its own!), so when adding or removing one of these packages, it's quite expected to have errors on the first run. In these cases, just recompile or remove the .aux file. There is no problem that I know of with \usepackage[french]{babel} if you don't use active characters in reference names (i.e., inside the argument of \label). I always use - instead of : in my label names for this reason (when writing in French). One more thing: hyperref must be loaded before cleveref! – frougon Apr 20 '20 at 14:47
  • The example works with hyperref, just see where I load it in the latest edit, and compile twice. Regarding package load order, see Should babel package call be placed at the end of the preamble?. – frougon Apr 20 '20 at 14:52
  • 1
    @frougon The \xspace trick might only be useful for adding the space which would be gobbled by a macro with no argument. Since spaces after mandatory arguments are preserved, doing the check is simply a waste of time. – egreg Apr 20 '20 at 15:00
  • @egreg Thanks for the explanation. I have used xspace very little all in all and that was long ago, so I didn't remember very well how it works. – frougon Apr 20 '20 at 15:11