# Syllables Source: https://pinyinjs.dev/syllables/ Index of every page: https://pinyinjs.dev/llms.txt Use the syllable functions to parse, validate and format written pinyin without loading a dictionary. ```ts import { isSyllable, readSyllable, writeSyllable } from "@kensio/pinyinjs"; readSyllable("jiù"); // { initial: "j", final: "iou", tone: 4 } readSyllable("jiu4"); // the same, both notations parse readSyllable("lv4"); // { initial: "l", final: "ü", tone: 4 } readSyllable("hello"); // undefined readSyllable("běi3"); // undefined, one notation at a time ``` These functions are useful for pinyin input fields, learner exercises and conversion between tone notations. ## Underlying forms `initial` and `final` store the underlying syllable components. For example, 就 contains `j` + `iou`, while 军 contains `j` + `ün`: ```ts readSyllable("jūn"); // { initial: "j", final: "ün", tone: 1 } readSyllable("jun1"); // the same ``` Formatting applies the spelling rules. It contracts `iou` to `iu` after an initial and removes the umlaut from `ün` after `j`. Different accepted spellings therefore produce the same underlying syllable. Choose a notation when formatting the syllable: ```ts const jiu = { initial: "j", final: "iou", tone: 4 } as const; writeSyllable(jiu); // "jiù" writeSyllable(jiu, "numbers"); // "jiu4" writeSyllable(jiu, "superscript"); // "jiu⁴" writeSyllable(jiu, "none"); // "jiu" ``` `writeSyllableSpelling` gives the toneless spelling on its own: ```ts import { writeSyllableSpelling } from "@kensio/pinyinjs"; writeSyllableSpelling({ initial: "j", final: "ün", tone: 1 }); // "jun" ``` ## Accepted input Input can use tone marks, tone digits, superscript digits, or `v` and `u:` for ü: ```ts readSyllable("lü4"); // { initial: "l", final: "ü", tone: 4 } readSyllable("lv4"); // the same readSyllable("lu:4"); // the same ``` A syllable must use one tone notation. Mixed notation such as `běi3` returns `undefined`. `normaliseUmlaut` converts the `v` and `u:` conventions separately: ```ts import { normaliseUmlaut } from "@kensio/pinyinjs"; normaliseUmlaut("lv"); // "lü" ``` ## Parsing and validation A successful parse means the spelling has a valid initial and final. Use an inventory check to determine whether Mandarin uses that combination: ```ts readSyllable("shong"); // { initial: "sh", final: "ong", tone: undefined } isSyllable("shong"); // true ``` For example, `shong` can be parsed but is absent from the standard Mandarin inventory: ```ts import { ATTESTED_SYLLABLES, DICTIONARY_SYLLABLES } from "@kensio/pinyinjs"; DICTIONARY_SYLLABLES.has("shong"); // false DICTIONARY_SYLLABLES.has("zhuang"); // true ATTESTED_SYLLABLES.length; // 415 ``` Choose the inventory appropriate to your input: | Export | Size | Is | | ---------------------- | ---: | ------------------------------------------------------ | | `ATTESTED_SYLLABLES` | 415 | the standard toneless syllable inventory | | `RARE_SYLLABLES` | 9 | spellings the dictionary uses that the inventory omits | | `DICTIONARY_SYLLABLES` | 424 | the two together, what the build validates against | The dictionary inventory additionally includes nine rare syllables, `bong`, `cei`, `din`, `eng`, `fiao`, `lo`, `rua`, `sei` and `tei`. They occur in dialect readings, interjections and onomatopoeia. Use `ATTESTED_SYLLABLES` for learner input and `DICTIONARY_SYLLABLES` when validating dictionary data. ## Valid tone combinations The syllable inventories omit tone. Some syllables use only a subset of the five tones. For example, `lo` occurs only in neutral tone, while `bàn` occurs but `bán` does not. ```ts import { isAttestedTone, readSyllable, SYLLABLE_TONES } from "@kensio/pinyinjs"; SYLLABLE_TONES.get("lo"); // [5] SYLLABLE_TONES.get("ban"); // [1, 3, 4, 5] isAttestedTone(readSyllable("ló")); // false isAttestedTone(readSyllable("lo")); // true, no tone claims nothing ``` The dictionary contains 1,708 of the 2,120 possible combinations of 424 syllables and five tones. A build assertion keeps the exported tone inventory consistent with those readings. [Romanisation readers](https://pinyinjs.dev/romanization/#the-tone-narrows-the-list) use this inventory to narrow ambiguous spellings. For example, Wade-Giles `lo²` can only represent 羅, `luó`. `isAttestedTone` accepts syllables outside its inventory without judging them. Use the inventory sets separately when validating the syllable itself. `INITIALS` has 21 entries and `FINALS` has 41, with `isInitial`, `isFinal` and `isPalatalInitial` beside them. ## Splitting written pinyin ```ts import { readWord, splitSyllables } from "@kensio/pinyinjs"; splitSyllables("nǐhǎo"); // ["nǐ", "hǎo"] splitSyllables("Xī'ān"); // ["Xī", "ān"] splitSyllables("yinhang"); // ["yin", "hang"] splitSyllables("guórén"); // ["guó", "rén"], not ["guór", "én"] splitSyllables("hǎiōu"); // ["hǎi", "ōu"], missing apostrophe, read anyway readWord("yínháng"); // the same, parsed into Syllable objects ``` Splitting uses valid syllable forms to choose boundaries. For example, `guórén` cannot become `guór` + `én` because `guór` is invalid. A missing apostrophe can be recovered when the boundary is unambiguous. ### Using tone marks to find boundaries Tone marks provide additional boundary information: ```ts splitSyllables("bùān"); // ["bù", "ān"], 不安 without its apostrophe splitSyllables("xīan1"); // ["xī", "an1"], 西安 half typed splitSyllables("xīa"); // ["xīa"], the mark misplaced on one syllable ``` Two tone marks cannot belong to one syllable. A misplaced mark and a syllable boundary have equal cost, and ties favour the longer syllable. This keeps `xīa` together. For `xīan1`, the tone information makes the complete reading cheaper than `xīa` + `n1`. Many Latin letter sequences can be parsed as pinyin, including English words: ```ts readWord("nonsense"); // [{ initial: "n", final: "o" }, { initial: "", final: "n" }, … ] ``` Validate the parsed pieces against `ATTESTED_SYLLABLES` when checking a pinyin input field. A successful `readWord` call alone does not establish that the input is pinyin. ## Tones ```ts import { applyToneMark, NEUTRAL_TONE, stripToneMarks, toneFromMarks, } from "@kensio/pinyinjs"; applyToneMark("hao", 3); // "hǎo" applyToneMark("hao", NEUTRAL_TONE); // "hao" applyToneMark("lü", 4); // "lǜ" stripToneMarks("hǎo"); // "hao" stripToneMarks("Xī'ān"); // "Xi'an" toneFromMarks("hǎo"); // 3 toneFromMarks("hao"); // undefined ``` `applyToneMark` places the mark on `a`, otherwise on `o` or `e`, otherwise on the last vowel. This puts the mark on `u` in `iu` and on `i` in `ui`. It replaces an existing mark and leaves text without a vowel unchanged. ### undefined and the neutral tone `Syllable.tone` distinguishes an explicit neutral tone from an unspecified tone: - `5` (`NEUTRAL_TONE`) means the syllable has neutral tone, as in the particle `de` in 我的. - `undefined` means the input did not specify a tone, as in `bei` in `beijing`. For example, `toneFromMarks("hao")` returns `undefined` because the spelling contains no tone mark. ## From the command line ```console $ pinyinjs syllable nǐhǎo nǐhǎo nǐ hǎo nǐ n + i, tone 3 nǐ ni3 ni³ hǎo h + ao, tone 3 hǎo hao3 hao³ ``` The `syllable` and `sandhi` commands run without loading a dictionary.