Directory macros/latex/contrib/xjyutping
xjyutping (LaTeX)
Version 1.5.0 (2026-10-02). Versions follow Semantic Versioning, and the history of the project is kept in CHANGELOG.md.
Overview
This project is a LaTeX package that adds Cantonese Jyutping (粵拼) above Traditional Chinese characters. It is aimed at anyone preparing Cantonese material in LaTeX, such as teaching notes, lyrics or poetry, where the pronunciation of each character should be shown above it.
Pronunciation is determined by looking at the context and cross-referencing it with a list of about 104 000 words. The package supports XeLaTeX and LuaLaTeX, and it was inspired by xpinyin, which adds Mandarin pinyin to Simplified Chinese characters. A Python version that gives the same readings is available as xjyutping-py.
A minimal document is,
\documentclass{article} \usepackage[fontset=none]{ctex} % compile with xelatex or lualatex \setCJKmainfont{Songti TC} % any Traditional Chinese font \usepackage{xjyutping} \begin{document} \begin{jyutpingscope} 我哋去銀行,行路返屋企。校長喺學校長大。 \end{jyutpingscope} \end{document}
Looking at the output, 行 is read hong4 in 銀行 but haang4 in 行路, while 長 is read zoeng2 in both 校長 and 長大.
Instead of ctex, a document can load xeCJK (XeLaTeX) or luatexja-fontspec (LuaLaTeX). If none of them is loaded, the package loads xeCJK or LuaTeX-ja itself. The manual, xjyutping-doc.pdf, describes every command and option in detail.
Installing
The package needs four files, xjyutping.sty, xjyutping.lua (used by LuaLaTeX), xjyutping-chars.def and xjyutping-words.def. Keep them together, either next to your document or in your personal tree (~/Library/texmf on macOS, ~/texmf on Linux). To copy them into the personal tree, run,
d="$(kpsewhich -var-value TEXMFHOME)/tex/latex/xjyutping" && mkdir -p "$d" && cp xjyutping.sty xjyutping.lua xjyutping-*.def "$d"
Commands
| Command | Effect |
|---|---|
\begin{jyutpingscope}[opts] … \end{jyutpingscope} |
Annotate every character in the block. |
\xjyutping*[opts]{text} |
Annotate a piece of running text. |
\xjyutping[opts]{字}{reading} |
Give a character or word your own reading, as in \xjyutping{銀行}{ngan4 hong4}. |
\setjyutping{字}{reading} |
Change a character's default reading. |
\setjyutping{詞}{readings} |
Add or change a word, as in \setjyutping{重話}{zung6 waa6}. |
\disablejyutping, \enablejyutping |
Turn annotation off and on inside a scope. |
\xjyutpingsetup{opts} |
Set options. They also work as package options. |
A scope always ends the paragraph, and its lines are spaced so that the Jyutping never touches the line above. The \setjyutping command is global and applies from the point where it appears, including the middle of a scope. However, since a scope is read in full before it is typeset, a \setjyutping inside a scope applies even within \iffalse … \fi. Hence conditional settings should be kept outside the scope.
Options
| Key | Default | Meaning |
|---|---|---|
ratio |
0.45 |
Size of the Jyutping relative to the text. |
vsep |
1.05em |
Height of the Jyutping's baseline above the character's. |
hsep |
0.15em plus 0.4em |
Space between characters. The stretch lets lines justify. |
width |
auto |
Cell width. With auto the width is the same for the whole block and fits its longest Jyutping, with natural each cell is as wide as it needs and a length such as 1.8em gives a fixed grid. |
font |
\normalfont |
Font of the Jyutping. |
format |
empty | Extra formatting, such as \color{gray}. |
multiple |
empty | Formatting for guessed readings of characters with several readings, such as \color{red} for proofreading. |
fancy |
false |
Show tones in the style of Visual Jyutping (Section "Fancy tones"). |
linebreak |
false |
Keep the lines of the source (Section "Verse and lyrics"). |
align |
justify |
Alignment of the lines of a scope, which is justify, left, centre (or center) or right. The values also work on their own, as in [linebreak,centre]. |
debug |
false |
Log how each run of text was read. |
In the debug log, w marks a word, u a reading you set, m a guessed reading (with the other readings in brackets) and s a character with only one reading.
Fancy tones
The fancy option replaces each tone number with a small stroke showing the pitch of the tone, followed by a smaller tone number, in the same way as Visual Jyutping writes jyutˍ₆ ping˗₃. The six tones are drawn as,
| Tone | Stroke | Number | Example |
|---|---|---|---|
| 1 | high level (ˉ) | raised | 詩 si1 |
| 2 | rising to high (ˊ) | raised | 史 si2 |
| 3 | mid level (˗) | lowered | 試 si3 |
| 4 | falling to low (ˎ) | lowered | 時 si4 |
| 5 | rising from low (ˏ) | lowered | 市 si5 |
| 6 | low level (ˍ) | lowered | 事 si6 |
Since the strokes are drawn rather than taken from a font, they work with any font and colour, and the spacing is adjusted to fit them. PDF bookmarks and the debug log keep the plain tone numbers.
Verse and lyrics
The linebreak option keeps the lines of the source, which suits verse and lyrics, while the align option sets the lines flush left, centred or flush right. For example,
\begin{jyutpingscope}[linebreak,centre] 床前明月光, 疑是地上霜。 舉頭望明月, 低頭思故鄉。 \end{jyutpingscope}
Specifically, each line end becomes a line break (\\), while a blank line leaves an empty line and starts a new paragraph. The new paragraph follows the settings of the document, so it is indented by default and not indented with \usepackage[parfill]{parskip}, whose paragraph space is added to the empty line. A \centering inside the scope also works, and an aligned scope starts a paragraph of its own. Since a line end also ends a run of characters, a word is never looked up across two lines.
However, linebreak only works in the environment. It has no effect in \xjyutping*, in a scope inside the argument of a command such as \parbox{…} or in a scope nested inside one without the option.
How readings are chosen
The reading of each character is chosen in three steps.
- First, the text is split into runs of Chinese characters. Punctuation, Latin text and most commands end a run, while formatting such as
\textbfor\colordoes not, so銀\textbf{行}is still read as the word 銀行. - Then each run is split into the fewest words from the word list, and each word takes its reading from the list. Where two splits have as many words and single characters, the one of the more common words wins, as counted in the word-frequency list of rime-cantonese and in the transcripts of WenetSpeech-Yue (步行|街 rather than 步|行街).
- Lastly, a character left on its own takes the reading set with
\setjyutping, or otherwise the reading given by the one or two characters after it, its reading at the end of a run or its default reading. For example, 呢 is the demonstrative ni1 before a classifier (呢張床) and the particle ne1 otherwise (你呢?, 佢呢就走).
A reading given with \xjyutping{…}{…} always takes priority, and a word set with \setjyutping wins a tie of step 2. Variant shapes such as 為/爲 and 裡/裏 find the same words.
XeLaTeX and LuaLaTeX
While the readings and options are the same under both engines, LuaLaTeX also needs xjyutping.lua, installed next to xjyutping.sty. Under LuaLaTeX,
- punctuation and line breaks follow the rules of LuaTeX-ja, so lines can break differently,
- a paragraph can be any length, while XeLaTeX stops at about 15 000 characters,
- compiling takes about 1.6 times as long and
- text that comes from a macro takes the
\setjyutpingreadings in force at the end of its paragraph.
pdfLaTeX is not supported.
Troubleshooting
\verb inside a scope gives an error
The body of a scope is read in full before it is typeset, in the same way as the argument of a command. Hence \verb and verbatim environments cannot go inside a scope, and a % in \url or \href must be written \%. Put verbatim material outside the scope.
Text from a macro is read one character at a time
Since the package only sees the name of a macro when it reads a scope, text that comes from a macro, from \maketitle or from an \included file is annotated one character at a time, without word context. Put \xjyutping* inside the macro, or a scope inside the included file.
A command's argument is annotated or left plain
The arguments of \label, \ref, \cite, \url, \includegraphics and similar commands are left alone. Other commands need their Chinese argument in braces, so write \textbf{行} rather than \textbf 行.
The table of contents has no Jyutping
Section titles, captions and footnotes inside a scope are annotated, while the table of contents is only annotated if \tableofcontents is itself inside a scope. Running heads and PDF bookmarks always stay plain.
A beamer frame title has no Jyutping
In beamer, a \frametitle inside a scope is typeset after the scope has ended. Write \frametitle{\xjyutping*{…}} instead.
Underlines break the spacing
The commands of xeCJKfntef, such as \CJKunderline, do not keep the spacing of the cells. Use \underline or \uline from ulem instead.
A character has no Jyutping
The character is missing from the data. Give it a reading with \setjyutping or \xjyutping, which both work for characters outside the data.
Data
The readings are stored in xjyutping-chars.def (30 089 characters) and xjyutping-words.def (about 104 000 words, with how often each is used). They are built by tools/build-data.py from six sources, which tools/fetch-sources.sh downloads,
- the Jyutping table of the Linguistic Society of Hong Kong (LSHK), for the character readings,
- rime-cantonese, for the default readings, the main word list and the word frequencies,
- ToJyutping, for the choice between the readings rime gives a word and for 512 more words,
- CC-Canto and the Cantonese readings of CC-CEDICT from Jyut Dictionary, for 2 531 more words,
- OpenCC, for variant shapes and
- 粵音資料集叢, for the readings of 640 rare characters.
The build also reads tools/wenetspeech-yue-counts.tsv, which counts how often each word is used in the 6.8 million transcribed utterances of WenetSpeech-Yue. Since rime-cantonese is treated as authoritative, the other sources only add what it lacks. The exceptions are ToJyutping, which may choose a different one of the readings that rime gives a word, since its choices follow Hong Kong usage (for example 公園 gung1 jyun2 and 郵局 jau4 guk2), and the hand-checked tables at the top of tools/build-data.py, which correct readings found wrong against the corpora below. To fix a reading,
- Edit the hand-checked tables at the top of
tools/build-data.py - Run
python3 tools/build-data.py
The script looks for the sources in the folder jyutData next to this repository (or in --sources DIR), and it also updates the data of xjyutping-py when that repository is next to this one.
Accuracy
We measured the accuracy on seven corpora. The Hong Kong Cantonese Corpus (HKCanCor) is a corpus of conversation recorded in the 1990s whose 161 045 characters were annotated with Jyutping by hand. On the half of its files that was kept out of the tuning, the package reads 95.6% of the characters correctly (94.0% in 1.4.0) and 97.9% of the characters outside sentence-final particles and interjections, while ToJyutping 3.2.0 reads 92.6% and 96.1%. On the particles of CantoMap, which were transcribed by ear, it reads 97.5% (79.0% in 1.4.0, 83.0% for ToJyutping). On fresh sentences of SpiCE, MagicHub and WenetSpeech-Yue, where the systems disagree, the reading of xjyutping was judged right in 91.7% of the cases, against 62.4% for ToJyutping. Part II, Section 10 of CHANGELOG.md describes the tests.
Testing
tests/run-tests.sh checks the readings under both engines, with and without fancy, while tests/render.sh renders a test file to PNG for a visual check, for example tests/verse-check.tex for linebreak and align.
Acknowledgements and attributions
This project was inspired by the LaTeX package xpinyin by Qing Lee (李清), which adds pinyin to Simplified Chinese characters.
The fancy option was inspired by Visual Jyutping by Vincent Tam, whose tone symbols it draws, and by the Visual Cantonese Fonts (粵語字體) by Jon Chui / A3I Ltd. (canto.hk, documentation, source). We would like to thank both of them.
We also thank the authors of the data sources,
- the Jyutping Workgroup of the Linguistic Society of Hong Kong (LSHK), for the Cantonese Pronunciation List of Characters for Computers (電腦用漢字粵語拼音表, lshk-org/jyutping-table, CC BY 4.0), together with Prof Lu Qin and Dr Cheung Kwan Hin of the Hong Kong Polytechnic University and Nathan Hammond, who are thanked there,
- the Cantonese Computational Linguistics Infrastructure Development Workgroup (CanCLID) and its contributors, for rime-cantonese (粵語拼音輸入方案, CC BY 4.0), which gives the default readings and most of the words, and for ToJyutping (BSD-2-Clause), whose word list chooses between rime's readings of a word and adds 512 words,
- 石見田, for 粵音資料集叢 (jyut.net, data at jyutnet/cantonese-books-data), together with the authors and editors of the thirteen dictionaries it digitises, namely 廣州話正音字典 (2004), 廣州話標準音字彙 (1988), 粵語同音字典 (1974/1996), 粵語查音識字字典 (1985), 同音字彙 (1971), 部身字典 (1967), The Student's Cantonese-English Dictionary (1947), 粵音韻彙 (1941), 道字典 (1941), 道漢字音 (1939), 民眾識字粵語拼音字彙 (1931), 廣話國語一貫未定稿 (1916) and 分部分音廣話九聲字宗 (1914),
- Aaron Tan, for Jyut Dictionary, which distributes CC-Canto (© 2015–17 Pleco Inc., cantonese.org, CC BY-SA 3.0) and the Cantonese readings for CC-CEDICT (© 2015 Pleco Software Inc., CC BY-SA 3.0), together with MDBG and the contributors of CC-CEDICT and
- Carbo Kuo (BYVoid) and the contributors of OpenCC, for OpenCC (Apache-2.0), whose variant tables let variant shapes find the same words.
Lastly, we thank the authors of the corpora that we used to tune the package and to measure its accuracy,
- Kang Kwong Luke, for the Hong Kong Cantonese Corpus (HKCanCor, CC BY 4.0), as distributed with PyCantonese,
- Grégoire Winterstein, Carmen Tang and Regine Lai, for CantoMap (GPL-3.0),
- Khia A. Johnson, Molly Babel, Ivan Fong and Nancy Yiu, for SpiCE (doi:10.5683/SP2/MJOXP3, CC BY 4.0),
- the ASLP-lab, for WenetSpeech-Yue (CC BY-NC 4.0), whose transcripts also give the word frequencies of
tools/wenetspeech-yue-counts.tsv, - Beijing Magic Data Technology, for the Guangzhou Cantonese Conversational Speech Corpus of MagicHub and
- the contributors of the Cantonese Wikipedia.
Licence
The code (xjyutping.sty, xjyutping.lua, tools/ and tests/) is released under the LaTeX Project Public License (LPPL) 1.3c, while the data files (xjyutping-chars.def and xjyutping-words.def) are released under CC BY-SA 4.0, since they adapt the CC BY-SA 3.0 word lists above. The word list of ToJyutping is used under the BSD 2-Clause License, whose notice is reproduced in LICENSE. The readings of the 640 characters from 粵音資料集叢 come from data published without a licence and are used with attribution. To build the data without them, empty BOOKS in tools/build-data.py. The word counts in tools/wenetspeech-yue-counts.tsv are counted from the transcripts of WenetSpeech-Yue (CC BY-NC 4.0); no text of any corpus is included. The full terms are given in LICENSE.
Download the contents of this package in one zip archive (2.4M).
Xjyutping – Automatically add Cantonese Jyutping to Traditional Chinese characters
xjyutping is a packaged that adds Cantonese Jyutping (粵拼) on top of Traditional Chinese Characters. Pronunciation is determined through looking at context and cross referencing with existing vocabulary. The package supports XeLaTeX and LuaLaTeX. It was inspired by the project xpinyin which adds mandarin pinyin to Simplified Characters. The code is under the LPPL 1.3c and the data under CC BY-SA 4.0.
| Package | Xjyutping |
| Home page | |
| Bug tracker | |
| Version | 1.5.0 |
| Licenses | The LaTeX Project Public License 1.3c CC BY-SA 4.0 |
| Maintainer | Longkai Li |
| Contained in | TeX Live as xjyutping MiKTeX as xjyutping |
| Topics | Use LuaTeX Use XeTeX Phonetic |