9

I get Hebrew numerals as page number, with this code:

\documentclass{book}
\usepackage{polyglossia}
\setdefaultlanguage{hebrew}
\setotherlanguage{english}
\usepackage{fontspec}
\newfontfamily\hebrewfont{Times New Roman}[Script=Hebrew]
\begin{document}
\pagenumbering{alph}    
\setcounter{page}{270}          
שלום
\end{document}

It's working as expected, and printing the page number "רע" which is 270 in Hebrew letters.

However, this word, "רע" means "bad", and it is a common practice to replace the letters order, to "ער". There are other examples of such "unwanted" letter combinations as well.

I assume that there isn't such ready made configuration, so I would like to implement it myself.

Is it possible to have some function called, and let it check in some look up table, to replace "unwanted" key with "good" value?

Can I get any reference to some similar functionality, that I will build on?

Or, any other solution to the problem of modifying the letters in the page number?

9
+50

It looks like \alph (or rather, \@alph) is redefined to use the macro \@hebrew@numeral, which produces these numbers. I'm redefining it to first look up if a number is bad and print something different if this is the case. The output will be unchanged otherwise.

Bad numbers are declared using \newbadnum{<number>}{<good representation>}, which I also define below.

Note: Like the original, this is a XeLaTeX document.

\documentclass{book}
\usepackage{polyglossia}
\setdefaultlanguage{hebrew}
\setotherlanguage{english}
\usepackage{fontspec}
\newfontfamily\hebrewfont{Times New Roman}[Script=Hebrew]

\makeatletter %% <- make @ usable in command names
\let\@hebrew@numeral@bad\@hebrew@numeral  %% <- store old definition
\newcommand*\@hebrew@numeral@good[1]{%    %% <- new definition
  \ifcsdef{badnum@\number#1}              %% <- if this number is bad
    {\csuse{badnum@\number#1}}            %% <- replace it
    {\@hebrew@numeral@bad{#1}}%           %% <- otherwise use old definition
}
\let\@hebrew@numeral\@hebrew@numeral@good %% <- replace old definition
\makeatother  %% <- revert @

%% Declare bad numbers:
\newcommand*\newbadnum[2]{\csdef{badnum@#1}{#2}}
\newbadnum{270}{ער}   %% <- literally bad
\newbadnum{271}{אאאא} %% <- for demonstration purposes
\newbadnum{272}{בבבב‬} %% <- for demonstration purposes
\newbadnum{274}{:-)}  %% <- I wonder if this will smile in reverse

\pagenumbering{alph}

\begin{document}

\setcounter{page}{270}

\textenglish{The page number is now correct: $\uparrow\uparrow$}

\begin{enumerate}
\item \textenglish{Also second level enumerations use these:}
    \begin{enumerate}\setcounter{enumii}{268}
    \item \textenglish{This is item \#\arabic{enumii}}
    \item \textenglish{This is item \#\arabic{enumii}}
    \item \textenglish{This is item \#\arabic{enumii}}
    \item \textenglish{This is item \#\arabic{enumii}}
    \item \textenglish{This is item \#\arabic{enumii}}
    \item \textenglish{This is item \#\arabic{enumii}} %% <- Still smiling :-)
    \end{enumerate}
\end{enumerate}

\end{document}

output

This also changes the behaviour of \hebrewnumeral, \Hebrewnumeral and \Hebrewnumeralfinal, which may not be desirable. (See the addendum below for how to avoid this.)

Here's a brief explanation of what a couple of the above lines do:

  • \newbadnum{270}{ער} calls \csdef{badnum@270}{ער}, which defines the macro \badnum@270 to expand to ער.

  • You can't call \badnum@270 directly because it contains an @ and digits. However, \csuse{badnum@270} does expand to ער if it was defined as above.

  • If you were to call \@hebrew@numeral@good{\somectr} and \somectr is a TeX counter (not a LaTeX counter) with value 270, it will effectively run \ifcsdef{badnum@270}{<true branch>}{<false branch>}. This tests if \badnum@270 is defined and chooses the appropriate branch.

  • The true branch is simply \csuse{badnum@\number#1}, so it'd expand to ער as described above.

  • The false branch just calls the original \@hebrew@numeral with the same argument.

The commands \csuse, \ifcsdef and \csdef are all from the etoolbox package, which was already loaded by polyglossia.



If there are a lot of bad numbers, you can replace the "Declare bad numbers" block by something like

%% Declare bad numbers:
\makeatletter %% <- make @ usable in command names
\newcommand*\newbadnum[2]{\csdef{badnum@#1}{#2}}
\newcommand*\newbadnums[1]{\forcsvlist\newbadnum@{#1}}
\def\newbadnum@#1{\newbadnum@@#1\newbadnum@@}
\def\newbadnum@@#1=#2\newbadnum@@{\newbadnum{#1}{#2}}
\makeatother  %% <- revert @
\newbadnums{{270}={ער},{271}={אאאא},{272}={בבבב},
            {274}={:-)}}

Edit: Interesting, the last line above looks fine in my editor, but it appears rather messed up here. It'll work if you copy-paste it, but I can't guarantee that it'll be human-readable. (The combination of left-to-right and right-to-left text in a single document is supremely confusing.)

Note that the =s are not allowed to be surrounded by spaces and that the ,s are not allowed to be preceded by spaces, but are allowed to be followed by spaces and/or a single newline. The braces ({…}) are entirely optional, but without them the combination of left-to-right and right-to-left text just confuses the hell out of both my text editor and this website (and me):

\newbadnums{270=ער,271=אאאא,272=בבבב,274=:-)}

(That's the same as the last two lines above, just without the {…} and the line break.)



Addendum

Here's a version that lets you specify separately what the \Hebrewnumeral numbers (used by \Alph) and the \Hebrewnumeralfinal numbers (not used?) look like. The syntax is

\newbadnum{<number>}{<hebrew>}{<Hebrew>}[<Hebrewfinal>]

where <number> is a number in arabic numerals and the other three arguments are the three relevant representations. The same value is used for <Hebrew> and <Hebrewfinal> if the optional argument is left out because it appears that these two representations usually coincide.

I must say that attempting post code fragments that contain Hebrew here is nightmare. The bottom four lines below appear rather mangled in my browser, but they're fine when I copy them to a text editor.

%% Declare bad numbers:
\makeatletter %% <- make @ usable in command names
\newcommand*\newbadnum[3]{\@testopt{\newbadnumopt{#1}{#2}{#3}}{#3}}
\def\newbadnumopt#1#2#3[#4]{%
  \csdef{badnum@#1}{%
    \if@gim@apost
      \if@gim@final #4\else #3\fi
    \else
      #2%
    \fi
  }%
}
\makeatother  %% <- revert @
%% The following looks fine in my editor, but not here.
\newbadnum{270}{ער}{ר״ע}          %% <- leave \Hebrew unchanged
\newbadnum{271}{אאאא}{אאאא}[רע״א] %% <- leave only \Hebrewfinal version unchanged
\newbadnum{274}{:-)}{:-D}[:-P]    %% <- In a good mood
\newbadnum{50}{נ}{נ׳}[ן׳]         %% <- The default

If you use numbers greater than (or equal to) 1000 some further modification may (or may not?) be required because \@hebrew@numeral is actually recursive for those. I don't know if these replacements should also be applied to parts of numbers.

  • Might want to declare the English words as left-to-right. – Davislor Jan 15 at 8:35
  • @Davislor: Yeah, I may have been a bit lazy there. (Now switching to English where appropriate.) – Circumscribe Jan 15 at 9:02
  • @Zvika: I've added an addendum about changing \Hebrew and \Hebrewfinal, in case you end up using those at some point. – Circumscribe Jan 15 at 15:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.