3

Based on this answer, I wrote a macro \CreateTheorem, which can be used in the following five ways:

  1. \CreateTheorem*{env}, which creates an unnumbered theorem-like environment env;
  2. \CreateTheorem{env}, which creates a numbered theorem-like environment env, numbered in order 1,2,3,...
  3. \CreateTheorem{env}[numberlike], which creates a numbered theorem-like environment env, shares the counter numberlike;
  4. \CreateTheorem{env}<numberwithin>, which creates a numbered theorem-like environment env, numbered within the counter numberwithin;
  5. \CreateTheorem*{env}(existedenv) or \CreateTheorem{env}(existedenv), which identifies the environment env with an existed environment existedenv;

    here \CreateTheorem*{env}(existedenv) has the same effect with \CreateTheorem{env}(existedenv*)

And to support multi-language, \CreateTheorem reads the macro \envnameEN, \envnameFR, \envnameDE etc. to get the label of the theorem-like environment env. It will then create environments envEN, envFR, envDE etc., and set the crefname accordingly, and finally define the environment env to select the appropriate environment based on the name of the current language.

Note that, if one writes \CreateTheorem*{env*}, then the language-specified environments will be named envEN*, envFR*, envDE* etc, not env*EN.

This macro is now part of the ProjLib toolkit: https://ctan.org/pkg/projlib. Since many commonly used theorem-like environments have been pre-defined, one can use it as in the following MWE:

\documentclass{article}

\usepackage{PJLthm}

\def\ideanameFR{Idée} \CreateTheorem{idea}[theorem]

\UseLanguage{French}

\begin{document}

\begin{theorem}\label{thm} Un théorème en français. \end{theorem} \begin{idea}\label{idea} Une idée en français. \end{idea}

\cref{thm,idea}

\end{document}

which resulted in:

enter image description here

However, the performance of the current implementation is relatively low. On my PC, the above MWE takes about 8s. The reason I believe this can be heavily improved is that if one removes those lines setting \crefname, the time would be reduced to 2s. Thus, I'm seeking a way to achieve the same result as the current \CreateTheorem, but only takes about 3s. Any suggestion is welcomed. Thanks in advance!


The current implementation of \CreateTheorem is:

\RequirePackage{PJLlang}
\RequirePackage{amsmath,amsthm}
\RequirePackage{aliascnt}
\PassOptionsToPackage{nameinlink}{cleveref}
\RequirePackage{cleveref}

\RequirePackage{xstring} \newcommand\PassFirstToSecond[2]{#2{#1}}% \NewDocumentCommand{\CreateTheorem}{sm}{% \begingroup \protected@edef\temp{#2}% \expandafter\IfEndWith\expandafter{\temp}{}{% \expandafter\StrGobbleRight\expandafter{\temp}{1}[\temp]% \PassFirstToSecond{}% }{% \PassFirstToSecond{}% }% {\expandafter\PassFirstToSecond% \expandafter{\temp}{\endgroup\InnerCreateTheorem{#1}}}% }% \NewDocumentCommand{\InnerCreateTheorem}{mmmod<>d()}{% % #1 = star or no star % #2 = name of environment % #3 = emptiness or star to append to name of environment % #4 = numbered like % #5 = numbered within % #6 = existed environment \IfBooleanTF{#1}{% \IfValueTF{#4} {@firstoftwo} {\IfValueTF{#5}{@firstoftwo}{@secondoftwo}}% }{% \IfValueTF{#4} {\IfValueTF{#5}{@firstoftwo}{@secondoftwo}}{ @secondoftwo} }% {% \GenericError{}% {\string\CreateTheorem\space syntax error\on@line}{% You cannot call the starred variant with optional argument,\MessageBreak nor call the unstarred variant with several optional arguments.}% {}% }{% \IfValueTF{#6}{% \IfBooleanTF{#1}{% \ifcsmacro{#2#3}% {\renewenvironment{#2#3}{\begin{#6}}{\end{#6}}}% {\newenvironment{#2#3}{\begin{#6}}{\end{#6}}}% }{% \ifcsmacro{#2#3}% {\renewenvironment{#2#3}{\begin{#6}}{\end{#6}}}% {\newenvironment{#2#3}{\begin{#6}}{\end{#6}}}% } }{% \IfBooleanTF{#1}{% \newtheorem{#2EN#3}{\csname#2nameEN\endcsname} \newtheorem{#2FR#3}{\csname#2nameFR\endcsname} \newtheorem{#2DE#3}{\csname#2nameDE\endcsname} \newtheorem{#2CN#3}{\csname#2nameCN\endcsname} \newtheorem{#2TC#3}{\csname#2nameTC\endcsname} \newtheorem{#2JP#3}{\csname#2nameJP\endcsname} \newtheorem{#2RU#3}{\csname#2nameRU\endcsname} }{% \IfValueTF{#5}{% \newcounter{#2#3}[{#5}]% \expandafter\renewcommand\expandafter% \csname the#2#3\expandafter\endcsname% \expandafter{\csname the#5\endcsname.\arabic{#2#3}}% }{% \IfValueTF{#4} {\newaliascnt{#2#3}{#4}} {\newcounter{#2#3}}% }% %--------------------------------------------------------------- \CreateTheoremNumberedLikeAliasCounter{#2}{EN}{#3}% \CreateTheoremNumberedLikeAliasCounter{#2}{FR}{#3}% \CreateTheoremNumberedLikeAliasCounter{#2}{DE}{#3}% \CreateTheoremNumberedLikeAliasCounter{#2}{CN}{#3}% \CreateTheoremNumberedLikeAliasCounter{#2}{TC}{#3}% \CreateTheoremNumberedLikeAliasCounter{#2}{JP}{#3}% \CreateTheoremNumberedLikeAliasCounter{#2}{RU}{#3}% %--------------------------------------------------------------- }% \NewDocumentEnvironment{#2#3}{} {\csname#2\csname\languagename ABBR\endcsname#3\endcsname}% {\csname end#2\csname\languagename ABBR\endcsname#3\endcsname}% } }% }% \NewDocumentCommand{\CreateTheoremNumberedLikeAliasCounter}{mmm}{% \newaliascnt{#1#2#3}{#1#3}% \newtheorem{#1#2#3}[{#1#2#3}]{\csname#1name#2\endcsname}% \aliascntresetthe{#1#2#3}% \crefname{#1#2#3}% {\csname#1name#2\endcsname}% {\csname#1name#2\endcsname}% \Crefname{#1#2#3}% {\csname#1name#2\endcsname}% {\csname#1name#2\endcsname}% }%

Jinwen
  • 8,518

2 Answers2

4

In your original question "Help on a macro \CreateTheorem(*)" I found the following preliminaries:

  1. The \CreateTheorem-mechanism is to work also when package(s) amsthm and/or cleveref are/is loaded.
  2. The underlying macros, whose arguments need to be arranged properly, are \newtheorem, \crefname, \Crefname, etc.

The problem with my current approach is that

  • \newtheorem is slow and is carried out often.
  • cleveref's macros \Crefname and \crefname are slow and are carried out often.
  • loading the package cleveref takes some time

You can see the slowness of \newtheorem etc by deriving a damaged/crippled variant of PJLthm.sty where

  • instances of \Crefname and \crefname and \newtheorem are replaced by instances of argument-gobbling-macros.
  • the loading of cleveref is removed.
  • package-name and version is changed.

When using the crippled/damaged variant of the package for benchmarking, make sure that no attempts take place at using the environments that should be defined as with the crippled/damaged variant defining in terms of underlying \newtheorem/\Crefname/\crefname does not take place and therefore with the crippled/damaged variant of the package environments that should be defined are not available.


I just downloaded ProjLib [2021/08/07a] from CTAN and compiled the .ins-files for obtaining the .sty-files.

When I save the example

\documentclass{article}

\makeatletter \def@gobblethree#1#2#3{}% \def@gobbleMandOptMand#1[#2]#3{}% \makeatother

\usepackage[originalref]{PJLthm}

\UseLanguage{French}

% For some reason the package defines the theorem-environment % via \AtEndPreamble. % Therefore theorems based on that cannot be defined within the preamble % unless via \AtEndPreamble or \AtBeginDocument or the like.

\AtEndPreamble{% \def\ideanameFR{Idée}% \CreateTheorem{idea}[theorem]% }%

\begin{document}

%%%\begin{theorem}\label{thm} %%% Un théorème en français. %%%\end{theorem} %%%\begin{idea}\label{idea} %%% Une idée en français. %%%\end{idea}

%%%\cref{thm,idea}

\end{document}

as test.tex and on Debian Linux do
time pdflatex test.tex

, then I get:

real    1m0,548s
user    1m0,284s
sys     0m0,134s

When I derive PJLthmDamaged.sty from PJLthm.sty by

  • replacing instances of \Crefname and \crefname and \newtheorem by instances of argument-gobbling-macros,
  • removing the loading of cleveref,
  • changing package-name and version,

so that the diff-file Patch.diff looks like this:

--- PJLthm.sty
+++ PJLthmDamaged.sty
@@ -1,23 +1,17 @@
 %%
-%% This is file `PJLthm.sty',
-%% generated with the docstrip utility.
-
-%% Copyright (C) 2021 by Jinwen XU
-%% 
-%% This is part of the ProjLib Toolkit.
-%% 
-%% This work may be distributed and/or modified under the conditions of the
-%% LaTeX Project Public License, either version 1.3c of this license or (at
-%% your option) any later version. The latest version of this license is in
-%% 
-%%     http://www.latex-project.org/lppl.txt
-%% 
-%% and version 1.3c or later is part of all distributions of LaTeX version
-%% 2005/12/01 or later.
+%% This is file `PJLthmDamaged.sty',
+%% generated by Ulrich Diez via manually editing
+%%
+%%   `PJLthm.sty',
+%%    Copyright (C) 2021 by Jinwen XU
 %% 
+%% in August 10, 2021, 18:24:37(UTC)
+%% in order to create a crippled/damaged variant which does not
+%% perform all the \Crefname/\crefname/\newtheorem.
+%%
 \NeedsTeXFormat{LaTeX2e}[2020-10-01]
-\ProvidesPackage{PJLthm}
-    [2021/08/07a Theorem setup and configuration]
+\ProvidesPackage{PJLthmDamaged}
+    [2021/08/10 Damaged!!!! theorem setup and configuration]
 \RequirePackage{kvoptions}
 \RequirePackage{etoolbox}
 \SetupKeyvalOptions{%
@@ -45,8 +39,8 @@
 \RequirePackage{PJLlang}
 \RequirePackage{amsmath,amsthm}
 \RequirePackage{aliascnt}
-\PassOptionsToPackage{nameinlink}{cleveref}
-\RequirePackage{cleveref}
+%%\PassOptionsToPackage{nameinlink}{cleveref}
+%%\RequirePackage{cleveref}

\NewDocumentCommand{\NameTheorem}{omm}{% \protected@edef\PJLthm@temp{#2}% @@ -176,8 +170,8 @@ {\IfValueTF{#5}{@firstoftwo}{@secondoftwo}}% }{% \IfValueTF{#4}

  •        {\IfValueTF{#5}{\@firstoftwo}{\@secondoftwo}}{
    
  •        \@secondoftwo}
    
  •        {\IfValueTF{#5}{\@firstoftwo}{\@secondoftwo}}{%%%%
    
  •        \@secondoftwo}%%%%
    
    }% {% \GenericError{}%

@@ -205,17 +199,17 @@ } }{% \IfBooleanTF{#1}{%

  •            \if@PJLlang@enable@EN\expandafter\PassFirstToSecond\expandafter{\csname#2nameEN\endcsname}{\newtheorem*{#2EN#3}}\fi%
    
  •            \if@PJLlang@enable@FR\expandafter\PassFirstToSecond\expandafter{\csname#2nameFR\endcsname}{\newtheorem*{#2FR#3}}\fi%
    
  •            \if@PJLlang@enable@DE\expandafter\PassFirstToSecond\expandafter{\csname#2nameDE\endcsname}{\newtheorem*{#2DE#3}}\fi%
    
  •            \if@PJLlang@enable@IT\expandafter\PassFirstToSecond\expandafter{\csname#2nameIT\endcsname}{\newtheorem*{#2IT#3}}\fi%
    
  •            \if@PJLlang@enable@PT\expandafter\PassFirstToSecond\expandafter{\csname#2namePT\endcsname}{\newtheorem*{#2PT#3}}\fi%
    
  •            \if@PJLlang@enable@BR\expandafter\PassFirstToSecond\expandafter{\csname#2nameBR\endcsname}{\newtheorem*{#2BR#3}}\fi%
    
  •            \if@PJLlang@enable@ES\expandafter\PassFirstToSecond\expandafter{\csname#2nameES\endcsname}{\newtheorem*{#2ES#3}}\fi%
    
  •            \if@PJLlang@enable@CN\expandafter\PassFirstToSecond\expandafter{\csname#2nameCN\endcsname}{\newtheorem*{#2CN#3}}\fi%
    
  •            \if@PJLlang@enable@TC\expandafter\PassFirstToSecond\expandafter{\csname#2nameTC\endcsname}{\newtheorem*{#2TC#3}}\fi%
    
  •            \if@PJLlang@enable@JP\expandafter\PassFirstToSecond\expandafter{\csname#2nameJP\endcsname}{\newtheorem*{#2JP#3}}\fi%
    
  •            \if@PJLlang@enable@RU\expandafter\PassFirstToSecond\expandafter{\csname#2nameRU\endcsname}{\newtheorem*{#2RU#3}}\fi%
    
  •            \if@PJLlang@enable@EN\expandafter\PassFirstToSecond\expandafter{\csname#2nameEN\endcsname}{\@gobbletwo{#2EN#3}}\fi
    
  •            \if@PJLlang@enable@FR\expandafter\PassFirstToSecond\expandafter{\csname#2nameFR\endcsname}{\@gobbletwo{#2FR#3}}\fi
    
  •            \if@PJLlang@enable@DE\expandafter\PassFirstToSecond\expandafter{\csname#2nameDE\endcsname}{\@gobbletwo{#2DE#3}}\fi
    
  •            \if@PJLlang@enable@IT\expandafter\PassFirstToSecond\expandafter{\csname#2nameIT\endcsname}{\@gobbletwo{#2IT#3}}\fi
    
  •            \if@PJLlang@enable@PT\expandafter\PassFirstToSecond\expandafter{\csname#2namePT\endcsname}{\@gobbletwo{#2PT#3}}\fi
    
  •            \if@PJLlang@enable@BR\expandafter\PassFirstToSecond\expandafter{\csname#2nameBR\endcsname}{\@gobbletwo{#2BR#3}}\fi
    
  •            \if@PJLlang@enable@ES\expandafter\PassFirstToSecond\expandafter{\csname#2nameES\endcsname}{\@gobbletwo{#2ES#3}}\fi
    
  •            \if@PJLlang@enable@CN\expandafter\PassFirstToSecond\expandafter{\csname#2nameCN\endcsname}{\@gobbletwo{#2CN#3}}\fi
    
  •            \if@PJLlang@enable@TC\expandafter\PassFirstToSecond\expandafter{\csname#2nameTC\endcsname}{\@gobbletwo{#2TC#3}}\fi
    
  •            \if@PJLlang@enable@JP\expandafter\PassFirstToSecond\expandafter{\csname#2nameJP\endcsname}{\@gobbletwo{#2JP#3}}\fi
    
  •            \if@PJLlang@enable@RU\expandafter\PassFirstToSecond\expandafter{\csname#2nameRU\endcsname}{\@gobbletwo{#2RU#3}}\fi
           }{%
               \IfValueTF{#5}{%
                   \newcounter{#2#3}[{#5}]%
    

@@ -241,7 +235,7 @@ \if@PJLlang@enable@RU\CreateTheoremNumberedLikeAliasCounter{#2}{RU}{#3}\fi% %--------------------------------------------------------------- }%

  •        \NewDocumentEnvironment{#2#3}{}
    
  •        \NewDocumentEnvironment{#2#3}{}%%%
               {\csname#2\csname\languagename ABBR\endcsname#3\endcsname}%
               {\csname end#2\csname\languagename ABBR\endcsname#3\endcsname}%
       }%
    

@@ -249,17 +243,17 @@ }% \NewDocumentCommand{\CreateTheoremNumberedLikeAliasCounter}{mmm}{% \newaliascnt{#1#2#3}{#1#3}%

  • \expandafter\PassFirstToSecond\expandafter{\csname#1name#2\endcsname}{\newtheorem{#1#2#3}[{#1#2#3}]}%
  • \expandafter\PassFirstToSecond\expandafter{\csname#1name#2\endcsname}{@gobbleMandOptMand{#1#2#3}[{#1#2#3}]}% \aliascntresetthe{#1#2#3}% \expandafter\PassFirstToSecond\expandafter{\csname#1name#2\endcsname}% {% \expandafter\PassFirstToSecond\expandafter{\csname#1name#2\endcsname}%
  •    {\crefname{#1#2#3}}%
    
  •    {\@gobblethree{#1#2#3}}%
    
    }% \expandafter\PassFirstToSecond\expandafter{\csname#1name#2\endcsname}% {% \expandafter\PassFirstToSecond\expandafter{\csname#1name#2\endcsname}%
  •    {\Crefname{#1#2#3}}%
    
  •    {\@gobblethree{#1#2#3}}%
    
    }%

}% \fi @@ -702,4 +696,4 @@

\endinput %% -%% End of file PJLthm.sty'. +%% End of filePJLthmDamaged.sty'.

and save the example

\documentclass{article}

\makeatletter \def@gobblethree#1#2#3{}% \def@gobbleMandOptMand#1[#2]#3{}% \makeatother

\usepackage[originalref]{PJLthmDamaged}

\UseLanguage{French}

% For some reason the package defines the theorem-environment % via \AtEndPreamble. % Therefore theorems based on that cannot be defined within the preamble % unless via \AtEndPreamble or \AtBeginDocument or the like.

\AtEndPreamble{% \def\ideanameFR{Idée}% \CreateTheorem{idea}[theorem]% }%

\begin{document}

%%%\begin{theorem}\label{thm} %%% Un théorème en français. %%%\end{theorem} %%%\begin{idea}\label{idea} %%% Une idée en français. %%%\end{idea}

%%%\cref{thm,idea}

\end{document}

as test.tex (same example as above, the only fifference is, that PJLthmDamaged.sty is loaded instead of PJLthm.sty) and on Debian Linux do
time pdflatex test.tex

, then I get:

real    0m1,614s
user    0m1,522s
sys     0m0,084s

The ratio
⟨compilation-time without disabling \newtheorem\crefname\Crefname⟩ : ⟨compilation-time with disabling \newtheorem\crefname\Crefname⟩ 
on my machine is about 38 : 1 !

You can see that with my current approach the slowness is not that much due to the code I wrote around the calls to \newtheorem etc for properly arranging the arguments of \newtheorem etc, but is more due to the frequency with which \newtheorem etc actually are carried out.

Thus I don't see ways of getting things compiled considerably faster other than reducing the amount of \newtheorem etc to carry out.

In this context, however, the requirement of \CreateTheorem also working while amsthm is in use is a problem—remembering the "joyful" comments about ams-things in the commented source code of previous hyperref package releases: When does a problem not have to do with ams-math-stuff? (-:

amsthm redefines internals of \newtheorem so that you cannot use a placeholder for the counter to use with the theorem to be defined.

Otherwise you could, e.g.,

  • have a macro \languagename which is to hold the name of the language currently in use

  • via \newcountr define a counter counter

  • temporarily define \languagename to be empty

  • do something like

    \newtheorem{theorem}[counter\languagename]{\csname theorem\languagename heading\endcsname}
  • restore \languagename

  • and for each ⟨languagename⟩ maintain an alias-counter whose name is of pattern counter⟨languagename⟩ and which is an alias of the "underlying" real counter counter and define macros whose names are of pattern \theorem⟨languagename⟩heading.

    (\languagename must temporarily be defined to be empty in order to prevent \newtheorem from delivering an error-message in case currently a language is in use where no language-specific alias-counter for the environment's real counter is defined yet.)

Without amsthm you can do this because without amsthm the tokens counter\languagename go into the definition of the environment in question unevaluated/unexpanded and are evaluated at the time of carrying out instances of the environment in question.

With amsthm, however, you can't do this because amsthm redefines things in a way where not the the tokens counter\languagename go into the definition of the environment in question but the control-word-token resulting from evaluating \csname thecounter\languagename\endcsname goes into the definition of the environment in question.

I.e., with amsthm the token \languagename is evaluated at the time of defining the environment in question and therefore doesn't make it into the definition of the environment in question.

When patching amsthm's redefinitions of the internals of \newtheorem so that—as is the case without amsthm—the tokens forming the name of the counter in use go into the definition of the environment in question unexpanded/unevaluated, then you would not need to define an internal theorem-environment theorem⟨languagename⟩ for each kind of theorem in each language, but one call to \newtheorem per group of theorems of same kind but differing in language would be sufficient.

If you don't mind patching the internal macro \@ynthm in case amsthm/cleveref is loaded—the patch could be arranged to be valid only during carrying out \CreateTheorem—, a solution according to the approach just outlined might be possible.

So far I have been reluctant to patch an internal macro, which, by the way, is also patched by other packages, for the following reasons:

  • E.g., the packages cleveref and hyperref in turn also patch the amsthm-code of \@ynthm. I did not yet delve into the code of cleveref deep enough to find out about all the details.
  • I don't know about the ways in which such patching would affect other packages about cross-referencing or theorems, e.g., the packages varioref or theorem.
  • Patching code of other packages implies the need of keeping track of changes to the code which your package is patching. This doesn't make maintenance of the package more easy.
  • Patching code of other packages implies the need of ensuring that your package in any case is loaded after those packages whose code it needs to patch. This doesn't make usage of the package more easy.

In case you are interested in such an approach despite its drawbacks, let me know and I might delve into the code of cleveref etc and edit this answer when I am done.

An initial experimental start, but one that needs intensive revision, debugging, adjusting, rectifying and thought, might be something like the following:

\documentclass[french,english,ngerman]{article}
\usepackage{babel}

\usepackage{xcolor} \definecolor{paper}{RGB}{255,255,255} \PassOptionsToPackage{nameinlink}{cleveref} \usepackage{hyperref} \usepackage{mathtools} \usepackage{amsthm}

\makeatletter \RequirePackage{aliascnt} \RequirePackage{cleveref} \crefdefaultlabelformat{#2#1#3} \RequirePackage{etoolbox} @ifdefinable@ynthmcopyA{\let@ynthmcopyA@ynthm} @ifdefinable@ynthmcopyB{\let@ynthmcopyB@ynthm} \newcommand\languagenamecopy{}

@ifpackageloaded{amsthm}{\iftrue}{\csname iffalse\endcsname}% \patchcmd{@ynthmcopyA}% {@xp\xdef\csname the#1\endcsname{@xp@nx\csname the#2\endcsname}}% {@xp\gdef\csname the#1\endcsname{\csname the#2\endcsname}}% {\message{!!! Patching successful !!!}}% {\message{!!! Patching failed !!!}}% \patchcmd{@ynthmcopyA}% {{#2}{\the\toks@}}% {{\unexpanded{#2}}{\the\toks@}}% {\message{!!! Patching successful !!!}}% {\message{!!! Patching failed !!!}}% @ifpackageloaded{cleveref}{\iftrue}{\csname iffalse\endcsname}% % !!! Probably some more things need to be done. % !!! E.g., I did not yet grasp what all this preamble-stuff in % !!! cleveref's redefinition of @ynthm is about. \patchcmd{@ynthmcopyA}% {@thm[#1]}% {@thm[#1@nx\languagename]}% {\message{!!! Patching successful !!!}}% {\message{!!! Patching failed !!!}}% \fi \fi

\makeatother

\makeatletter \newtheoremstyle{simple}% {}{}% {\normalfont}{}% {\normalfont}{}% {0pt}% {% \thmname{\MakeUppercase{#1}}% \thmnumber{ #2}% \hspace{.4em}% \textcolor{gray!55!paper}{$|$}% \hspace{.4em} \color{gray}% \thmnote{\ensuremath{(\text{#3})}~~}\pushQED{\qed}% } % One needs to see if something can be done about this: \def@endtheorem{\popQED\endtrivlist@endpefalse}% % because the redifinition applies to all theorem-environments, not just % those defined with theoremstyle "simple". Probably the new % environment-hook-management of up-to-date LaTeX-kernels can be used.

\renewcommand{\qedsymbol}{\makebox[1em]{\color{gray!55!paper}\rule[-0.1em]{.95em}{.95em}}}% \makeatother

\newcounter{theorem}

\newaliascnt{theoremenglish}{theorem}% \aliascntresetthe{theoremenglish}% \newcommand\theoremenglishheading{Theorem}% \newcommand\theoremenglishname{Theorem}% \crefname{theoremenglish}{theorem}{Theorem}%

\newaliascnt{theoremfrench}{theorem}% \aliascntresetthe{theoremfrench}% \newcommand\theoremfrenchheading{Théorème}% \newcommand\theoremfrenchname{Théorème}% \crefname{theoremfrench}{théorème}{Théorème}%

\newaliascnt{theoremngerman}{theorem}% \aliascntresetthe{theoremngerman}% \newcommand\theoremngermanheading{GTheorem}% \newcommand\theoremngermanname{GTheorem}% \crefname{theoremngerman}{gtheorem}{GTheorem}%

\theoremstyle{simple}

\makeatletter \let@ynthm@ynthmcopyA \let\languagenamecopy\languagename \let\languagename\empty \newtheorem{theorem}[theorem\languagename]{\csname theorem\languagename heading\endcsname} \let@ynthm@ynthmcopyB \let\languagename\languagenamecopy \makeatother

\begin{document}

\begin{theorem}\label{theoremA}% bla bla \end{theorem}

\selectlanguage{french}

\begin{theorem}\label{theoremB}% blu blu \end{theorem}

\ref{theoremA}\ \pageref{theoremA}\ \autoref{theoremA}\ % \nameref{theoremA}\ \cref{theoremA,theoremB}

\ref{theoremB}\ \pageref{theoremB}\ \autoref{theoremB}\ % \nameref{theoremB}\ \cref{theoremA,theoremB}

\end{document}

enter image description here

I have some questions regarding the "user interface":

  1. How about following the convention of naming languages as they are named in the babel package? Then you could get the name of the language currently in use from the macro \languagename. And you could simply use babel's infrastructure (\selectlanguage, \foreignlanguage etc) to change the language. babel's infrastructure would automatically set/adjust \languagename. Language specific defined structures would have names that can be obtained using the expansion of the macro \languagename. E.g. not "counterFR" but "counterfrench or "counterenglish", so that one could also say "counter\languagename" in general.

  2. What about using expl3 for adding an argument with comma-list/key=value-syntax for those cases where \CreateTheorem is used for creating numbered theorems? Something like

    \CreateTheorem{MyTheorem}[superordinate counter]{%
      EN={%
         crefname={bla}{bla}{bla}, %<- for cleveref's \crefname with aliascounter MyTheoremEN
         Crefname={bla}{bla}{bla} %<- for cleveref's \Crefname with aliascounter MyTheoremEN
         name=bla, %<- for the macro \MyTheoremENname
         autoref=bla,%<- for the macro \MyTheoremENautorefname with aliascounter MyTheoremEN
         heading=Theorem heading in English %<- for the macro \MyTheoremENheading
      },
      FR={%
         crefname={bla}{bla}{bla}, %<- for cleveref's \crefname with aliascounter MyTheoremFR
         Crefname={bla}{bla}{bla} %<- for cleveref's \Crefname with aliascounter MyTheoremFR
         name=bla, %<- for the macro \MyTheoremFRname 
         autoref=bla,%<- for the macro \MyTheoremFRautorefname with aliascounter MyTheoremFR
         heading=Titre du théorème en français  %<- for the macro \MyTheoremFRheading
      },
      %...
    }%
    

    An initial experimental start, but one that needs intensive revision, debugging, adjusting, rectifying and thought :-) , might be something like the following:

    \documentclass{article}
    

    \RequirePackage{xparse} \RequirePackage{aliascnt} \RequirePackage{cleveref}

    \ExplSyntaxOn

    % Helper-macros/scratch-macros %----------------------------- \cs_new:Nn \MYMODULE_exchange_i_iii_ii:nnn { #1 {#3} {#2} } \cs_new:Nn __MYMODULE_languageprefix: {} \cs_new:Nn __MYMODULE_countername: {}

    % Message-management: %-------------------- \msg_new:nnnn {MYMODULE} {Undefined Language Dependent Specification Class} {\token_to_str:N \CreateTheorem :\ Value\ \tl_to_str:n{#2}'\ for\ invalid\ key\#1'.} {Providing\ Language-dependent\ specifications\ for\ element\ `#1'\ is\ currently\ not\ implemented.} % As long as MYMODULE is not a real package but just some preamble-code redirect % MYMODULE-related messages to denote to the preamble instead of a package/module: \prop_gput:Nnn \g_msg_module_type_prop { MYMODULE } {} \prop_gput:Nnn \g_msg_module_name_prop { MYMODULE } {Preamble-Code}

    % Nested key=value-interface by means of package l3keys: %------------------------------------------------------- % (l3keys is similar to pgfkeys.) % The outer level of key=value-specifications, i.e., <language-ID>={...}, % is processed via \keyval_parse:nnn which was added tpo expl3 in 2020/12/19. % The inner level of key=value-specifications, % i.e., the single keys whose values are to be specified dependant on the language, % i.e., the "..."-content of the outer lever's {...}, % is processed via \keys_set:nn . % The keys for the inner level are defined via \keys_define:nn .

    \NewDocumentCommand \CreateTheoremSetKeys { mm } { % #1 = name of counter % #2 = keyval-list of language-specifications \cs_set:Nn __MYMODULE_countername: {#1} \keyval_parse:nnn { \MYMODULE_exchange_i_iii_ii:nnn { \MYMODULE_setlanguagespecificparameters:nn } {} } { \MYMODULE_setlanguagespecificparameters:nn } { #2 } }

    \cs_new_protected:Nn \MYMODULE_setlanguagespecificparameters:nn { % #1 = language-prefix % #2 = key-val-list for language whose prefix is language-prefix \cs_set:Nn __MYMODULE_languageprefix: {#1} \newaliascnt {__MYMODULE_countername: __MYMODULE_languageprefix:} {__MYMODULE_countername:}% \aliascntresetthe {__MYMODULE_countername: __MYMODULE_languageprefix:}% \keys_set:nn { MYMODULE } { #2 } }% \keys_define:nn { MYMODULE } { crefname.code:n = \cs_if_exist:NTF \crefname { \exp_args:Nx \crefname {__MYMODULE_countername: __MYMODULE_languageprefix: }#1 }{}, crefname.value_required:n = true, Crefname.code:n = \cs_if_exist:NTF \Crefname { \exp_args:Nx \Crefname {__MYMODULE_countername: __MYMODULE_languageprefix: }#1 }{}, Crefname.value_required:n = true, name.code:n = \exp_args:Nx \cs_set:cpn {__MYMODULE_countername: __MYMODULE_languageprefix: name} {#1}, name.value_required:n = true, autorefname.code:n = \exp_args:Nx \cs_set:cpn {__MYMODULE_countername: __MYMODULE_languageprefix: autorefname} {#1}, autorefname.value_required:n = true, theoremheading.code:n= \exp_args:Nx \cs_set:cpn {__MYMODULE_countername: __MYMODULE_languageprefix: heading} {#1}, theoremheading.value_required:n = true, the.code:n= \exp_args:Nx \cs_set:cpn {the __MYMODULE_countername: __MYMODULE_languageprefix: } {#1}, the.value_required:n = true, % % Define keys for more Language Dependent Specification Classes. % unknown.code:n = \msg_error:nnxx {MYMODULE} {Undefined Language Dependent Specification Class} {\exp_args:No \exp_not:n \l_keys_key_str} {\exp_not:n{#1}}, }

    \ExplSyntaxOff

    \newcounter{oddity}% % By default \theoddity is now s.th. like \arabic{oddity}. % % Assume a \CreateTheorem-command is in progesss and has already defined % - a theroem-like environment "oddity", using the "oddity\languageplaceholder"-counter, % and where the heading is to come from the macro \oddity<languageplaceholder>heading . % - a real counter "oddity" which is to be aliased to counters "oddity<languageplaceholder>" % for language-specific output related to that counter. % - a macro "\theoddity". % % For defining language-specific alias-counter-related supplementary macros % which do not get defined by \newaliascnt, the macro \CreateTheorem could % now internally do: % \CreateTheoremSetKeys{oddity}{% FR={% crefname={folie}{folies}, Crefname={Folie}{Folies}, name=folie, autorefname=folie, theoremheading=Folie, }, EN={% crefname={oddity}{oddities}, Crefname={Oddity}{Oddities}, name=oddity, autorefname=oddity, theoremheading=Oddity, the=\roman{oddity} }, DE={% crefname={Seltsamkeit}{Seltsamkeiten}, Crefname={Seltsamkeit}{Seltsamkeiten}, name=Seltsamkeit, autorefname=Seltsamkeit, theoremheading=Seltsamkeit, the=\alph{oddity}, }, }%

    \begin{document}

    \ttfamily

    \def\printmeaning#1{\string#1 = \meaning#1} \def\printmeanings#1#2{% \par{% \parskip=0ex \frenchspacing \hrule\hfill\par \noindent\strut Counter: #1\ \noindent Language: #2\ supplementary macros for alias-counter #1#2:\strut\par \hrule\hfill\par \noindent% \expandafter\printmeaning\csname cref@#1#2@name\endcsname \ \expandafter\printmeaning\csname Cref@#1#2@name\endcsname \ \expandafter\printmeaning\csname cref@#1#2@name@plural\endcsname \ \expandafter\printmeaning\csname Cref@#1#2@name@plural\endcsname \ \expandafter\printmeaning\csname #1#2name\endcsname \ \expandafter\printmeaning\csname #1#2autorefname\endcsname \ \expandafter\printmeaning\csname #1#2heading\endcsname \ \expandafter\printmeaning\csname the#1#2\endcsname\strut\par \hrule\hfill\par }% }%

    \printmeanings{oddity}{FR}

    \vfill

    \printmeanings{oddity}{EN}

    \vfill

    \printmeanings{oddity}{DE}

    \vfill\vfill

    \end{document}

    enter image description here

    Pitfalls I am currently aware of:

    With the keys of crefname and Crefname currently it is not checked whether the values consist of exactly two non-optional arguments and thus fit the syntax of \crefname/\Crefname.




When you have PJLthm.sty ([2021/08/07a]) and Patch.diff in the same directory, then in Debian Linux you can obtain the crippled/damaged PJLthmDamaged.sty, where

  • instances of \Crefname and \crefname and \newtheorem are replaced by instances of argument-gobbling-macros,
  • the loading of cleveref is removed,
  • package-name and version is changed,

by opening a shell, changing to that directory and performing the command
patch -o PJLthmDamaged.sty PJLthm.sty Patch.diff

(I could not insert PJLthmDamaged.sty into this answer directly as by doing so this answer would have exceeded the 30000-character limit.)

Ulrich Diez
  • 28,770
  • Thank you for your detailed analysis! There are still two problems that confuse me though: 1) The regionalref version, now as the default choice by PJLthm, also uses cleveref and \newtheorem, only calls them fewer times, but is considerably faster than the originalref version. Thus I've been supposing that is it possible that too many \crefnames or so somehow use too much memory and then cause a steep slow down? – Jinwen Aug 11 '21 at 01:02
  • You said that "The choice of underlying mechanisms, however, was made by you in your original question", what does this mean? I checked my old question, but it seems that I was just using the manually expanded version of \CreateTheorem, only faster because there were only three languages supported at that time.
  • – Jinwen Aug 11 '21 at 01:02
  • @Jinwen Thanks to whiplash, I am currently being kept awake at night by headaches, neck pain, dizziness, etc. I went to the computer to distract myself from it by dealing with LaTeX. Therefore, when I formulated my answer yesterday, my concentration was poor. Accordingly, I have formulated poorly. I ask you to accept my apologies. I have now edited and hope that the edits resolve the questions. Otherwise, please do not hesitate to follow up. :-) – Ulrich Diez Aug 11 '21 at 20:32
  • Thank you for the suggestion on patching \@ynthm, I didn’t tried this method but it seemed promising. Regarding the two suggestions you added in the last, I think the first is not necessary since there’s now (as of version 2021/08/11) a \StrToABBR that converts string like \languagename to the abbreviation. Your second suggestion is great, and I will try to achieve such an interface when I have the time. Hope you healthy, and many thanks for writing this wonderful answer! – Jinwen Aug 12 '21 at 23:50
  • @Jinwen Thank you for the good wishes. :-) I wish you and those you love well-being and good health. I just thought a bit about the mentioned interface and edited my answer and, hoping it will be useful for you, added a bit of code based on expl3 / package l3keys. – Ulrich Diez Aug 13 '21 at 19:02
  • Thank you for the expl3 code. I posted a possible use of your \CreateTheoremSetKeys as an answer below (its length does not fit in a comment), unfortunately no MWE is given due to the length constraints. The code is a little mess, mixing LaTeX2e and expl3 (I'm not yet familiar with expl3 so currently I'm not able to rewrite the rest codes with it). What's your opinion of this? – Jinwen Aug 14 '21 at 04:00
  • @Jinwen It's always a bit of a problem here on TeX LaTeX-Stack Exchange that you don't know in which areas of the wide field of TeX a questioner is more versed. As for LaTeX3/expl3: I recommend changing the module name from "MYMODULE" to something more appropriate to the package. Replacing the phrase "MYMODULE" everywhere with something more appropriate and under "Message Management" disabling the two lines \prop_gput:Nnn \g_msg_module_type_prop... and \prop_gput:Nnn \g_msg_module_name_prop... should be sufficient. ... – Ulrich Diez Aug 14 '21 at 10:26
  • @Jinwen The interweaving of LaTeX 2e code and expl3 code, which is currently also given in the LaTeX kernel, I don't see as a problem: The expl3 code is closed between \ExplSyntaxOn and \ExplSyntaxOff and for this code no \makeatletter would be necessary even if it would not be in a .sty file. If it were otherwise, it might be an indication of some kind of interweaving not intended by the developers. interface3.pdf takes some time getting used to at first, in my opinion, but it does a good job of explaining the ... – Ulrich Diez Aug 14 '21 at 10:26
  • @Jinwen ... of explaining the conventions that apply to latex3 and the structures of the l3keys package that are used in my code. Instead of using expl3/l3keys you could probably also implement things in terms of the structures provided by the package pgfkeys, which belongs to TikZ/pgf. I chose expl3/l3keys because - unlike pgfkeys/TikZ - it is included with current LaTeX kernels by default. – Ulrich Diez Aug 14 '21 at 10:36
  • Thank you for the suggestion, I've edited the code accordingly. And also thanks for mentioning interface3.pdf, it never occurs to me before that this is actually a detailed reference guide to expl3. – Jinwen Aug 14 '21 at 10:52