Jyutping Romanization

Among the most common tasks in handling Cantonese corpus data are those that involve the processing of Jyutping romanization. A common need is to convert Cantonese characters to Jyutping romanization. Another functionality of interest is the ability to convert Jyutping into other romanization schemes still used today. Whether you have data in Jyutping from a corpus reader or you have independently ingested Jyutping as Python strings, PyCantonese provides tools for these use cases.

Characters-to-Jyutping Conversion

The function characters_to_jyutping() takes a string of Cantonese characters and returns its word-segmented version with Jyutping romanization:

>>> import pycantonese
>>> pycantonese.characters_to_jyutping('香港人講廣東話')  # Hongkongers speak Cantonese
[('香港人', 'hoeng1gong2jan4'), ('講', 'gong2'), ('廣東話', 'gwong2dung1waa2')]

The characters-to-Jyutping conversion model is based on two data sources: (i) the HKCanCor corpus data included in the PyCantonese library, and (ii) the rime-cantonese data (the 2021.05.16 release, CC BY 4.0 license). Any unseen character, Cantonese or otherwise, is represented by None in the output.

To further process the Jyutping strings, please see Parsing Jyutping Strings.

A Cantonese character may have multiple pronunciations, most commonly due to pinjam (變音, “changed tone”). Whether the function characters_to_jyutping() can intelligently output the correct, contextually dependent pronunciation depends on whether the underlying data contains the relevant tokens. Example:

>>> import pycantonese
>>> # The correct pronunciation of 蛋 is with tone 2 (high-rising) as a standalone word.
>>> pycantonese.characters_to_jyutping('蛋')  # egg
[('蛋', 'daan2')]
>>> # The correct pronunciation of 蛋 is with tone 6 (low-level) in 蛋糕.
>>> pycantonese.characters_to_jyutping('蛋糕')  # cake
[('蛋糕', 'daan6gou1')]

Because characters_to_jyutping() performs word segmentation under the hood (via segment()), it is possible to customize word segmentation by passing in a Segmenter instance to the segmenter keyword argument of characters_to_jyutping().

>>> import pycantonese
>>> from pycantonese.word_segmentation import Segmenter
>>> # Create a `Segmenter` class instance.
>>> # See its documentation for what customization it allows.
>>> # As an example, the `disallow` parameter can take an iterable of strings
>>> # that represent words that you don't want to treat as words.
>>> # Here, let's pretend that you don't want 蛋糕 to be segmented as a single word.
>>> my_segmenter = Segmenter(disallow={"蛋糕"})
>>> pycantonese.characters_to_jyutping("蛋糕", segmenter=my_segmenter)
[('蛋', 'daan2'), ('糕', 'gou1')]

Parsing Jyutping Strings

Converting Jyutping to other romanization schemes necessitates the ability to parse Jyutping for the various phonological components (onset, nucleus, coda, and tone). To this end, PyCantonese exposes the function parse_jyutping() which parses a string of Jyutping romanization and returns a list of Jyutping objects; the string may contain results for multiple Chinese characters.:

>>> import pycantonese
>>> pycantonese.parse_jyutping('hou2')  # 好 good
[Jyutping(onset='h', nucleus='o', coda='u', tone='2')]
>>> pycantonese.parse_jyutping('gwong2dung1waa2')  # 廣東話 Cantonese
[Jyutping(onset='gw', nucleus='o', coda='ng', tone='2'),
 Jyutping(onset='d', nucleus='u', coda='ng', tone='1'),
 Jyutping(onset='w', nucleus='aa', coda='', tone='2')]

Syllabic nasals are treated as nuclei:

>>> import pycantonese
>>> pycantonese.parse_jyutping('m4goi1')  # 唔該 thank you / please
[Jyutping(onset='', nucleus='m', coda='', tone='4'),
 Jyutping(onset='g', nucleus='o', coda='i', tone='1')]

The function parse_jyutping() is able to detect invalid Jyutping romanization:

>>> import pycantonese
>>> pycantonese.parse_jyutping('hou7')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.9/dist-packages/pycantonese/jyutping.py", line 197, in parse_jyutping
    raise ValueError('tone error -- ' + repr(jp))
ValueError: tone error -- 'hou7'

The Jyutping class makes it easy to access the onset, nucleus, coda, and tone using the attribute syntax. It is also straightforward to retrieve the string representation and final (= nucleus + coda; 韻母):

>>> from pycantonese.jyutping import Jyutping
>>> jp = Jyutping(onset="j", nucleus="yu", coda="t", tone="6")
>>> jp.onset
>>> jp.nucleus
>>> jp.coda
>>> jp.tone
>>> str(jp)
>>> jp.final

Jyutping-to-Yale Conversion

The Yale romanization is still a commonly used system, particularly in numerous dictionaries and Cantonese language teaching resources. PyCantonese provides the jyutping_to_yale() function which reads a valid Jyutping string and returns the Yale equivalent:

>>> import pycantonese
>>> pycantonese.jyutping_to_yale('m4goi1')  # 唔該 thank you / please
['m̀h', 'gōi']
>>> pycantonese.jyutping_to_yale('gwong2dung1waa2')  # 廣東話 Cantonese
['gwóng', 'dūng', 'wá']

jyutping_to_yale() has the keyword argument as_list. When set to be False, it turns the returned value into a string.

>>> import pycantonese
>>> pycantonese.jyutping_to_yale('gwong2dung1waa2', as_list=False)  # 廣東話 Cantonese

While getting a string instead of a list might seem trivial enough that as_list would be necessary, its usefulness arises when there is potential confusion. In Yale romanization, a consonant letter or the low-tone marker “h” can be ambiguous as an onset of a syllable or as part of the previous syllable. When such ambiguity is detected, as_list=False automatically adds the quote character ' as a separator to disambiguate:

>>> import pycantonese
>>> pycantonese.jyutping_to_yale('hei3hau6', as_list=False)  # 氣候 climate
>>> # 'heihauh' would be ambiguous between hei3hau6 and hei6au6.

Jyutping-to-TIPA Conversion

PyCantonese also offers the jyutping_to_tipa() function for the LaTeX TIPA users:

>>> import pycantonese
>>> pycantonese.jyutping_to_tipa('m4goi1')  # 唔該 thank you / please
['\\s{m}21', 'kOY55']
>>> pycantonese.jyutping_to_tipa('gwong2dung1waa2')  # 廣東話 Cantonese
['k\\super w ON25', 'tUN55', 'wa25']

Currently, tones are output as Chao tone letters (= the numbers from 1 to 5) directly suffixed to the individual syllable string. (This may change in a future release if this behavior proves to be inconvenient.)