@kensio/pinyinjs 1.5.2 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +283 -67
- package/README.md +44 -3
- package/data/core.entries +82 -81
- package/data/core.freq +0 -0
- package/data/core.keys +1 -0
- package/data/full.entries +4897 -4899
- package/data/full.freq +0 -0
- package/data/full.keys +0 -2
- package/data/manifest.json +12 -12
- package/data/script.map +27 -48
- package/data/standard.entries +438 -437
- package/data/standard.freq +0 -0
- package/data/standard.keys +1 -0
- package/dist/cli/commands.d.ts.map +1 -1
- package/dist/cli/commands.js +22 -1
- package/dist/cli/commands.js.map +1 -1
- package/dist/cli/main.js +0 -0
- package/dist/decode/convert.d.ts +42 -0
- package/dist/decode/convert.d.ts.map +1 -1
- package/dist/decode/convert.js +140 -17
- package/dist/decode/convert.js.map +1 -1
- package/dist/decode/decode.d.ts +3 -2
- package/dist/decode/decode.d.ts.map +1 -1
- package/dist/decode/decode.js +15 -10
- package/dist/decode/decode.js.map +1 -1
- package/dist/decode/hints.d.ts +138 -0
- package/dist/decode/hints.d.ts.map +1 -0
- package/dist/decode/hints.js +214 -0
- package/dist/decode/hints.js.map +1 -0
- package/dist/decode/lattice.d.ts +9 -1
- package/dist/decode/lattice.d.ts.map +1 -1
- package/dist/decode/lattice.js +5 -3
- package/dist/decode/lattice.js.map +1 -1
- package/dist/decode/reading-rules.d.ts +28 -0
- package/dist/decode/reading-rules.d.ts.map +1 -1
- package/dist/decode/reading-rules.js +157 -0
- package/dist/decode/reading-rules.js.map +1 -1
- package/dist/dictionary/assertions.d.ts.map +1 -1
- package/dist/dictionary/assertions.js +8 -0
- package/dist/dictionary/assertions.js.map +1 -1
- package/dist/dictionary/frequency-table.js +1 -1
- package/dist/dictionary/locale.js +2 -2
- package/dist/dictionary/merge.d.ts.map +1 -1
- package/dist/dictionary/merge.js +30 -1
- package/dist/dictionary/merge.js.map +1 -1
- package/dist/format/html.d.ts +32 -0
- package/dist/format/html.d.ts.map +1 -1
- package/dist/format/html.js +144 -0
- package/dist/format/html.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/sources/unihan.d.ts +13 -4
- package/dist/sources/unihan.d.ts.map +1 -1
- package/dist/sources/unihan.js +63 -13
- package/dist/sources/unihan.js.map +1 -1
- package/package.json +28 -26
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,169 @@ which a dictionary rebuild can change, and the artifact format under `data/`.
|
|
|
9
9
|
|
|
10
10
|
## Unreleased
|
|
11
11
|
|
|
12
|
+
## 1.6.0
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- **Hanzi and pinyin together, rather than one instead of the other.**
|
|
17
|
+
`convertToAnnotatedHtml` writes the characters with their reading above them,
|
|
18
|
+
as `<ruby>` markup — the output every other mode could not give, and what a
|
|
19
|
+
learner's text, a subtitle and a dictionary entry all want. `convert` and
|
|
20
|
+
`convertToHtml` write the reading _instead of_ the characters.
|
|
21
|
+
`toAnnotatedHtml(pieces)` renders pieces you already have, and the command
|
|
22
|
+
line has `pinyinjs annotate`.
|
|
23
|
+
|
|
24
|
+
Each `<rt>` holds exactly what `convertToHtml` would have written, so tone
|
|
25
|
+
classes and uncertainty marking work inside an annotation as they do outside
|
|
26
|
+
one. `<rp>` gives the parenthesised fallback, the `<ruby>` carries `lang="zh"`
|
|
27
|
+
and the syllables inside it keep `zh-Latn-CN-pinyin`, since on an English page
|
|
28
|
+
a screen reader needs to be told both.
|
|
29
|
+
|
|
30
|
+
**A base is not always one character**, which is what makes this harder than
|
|
31
|
+
it looks and what every naive per-character annotation gets wrong. 玩儿 is two
|
|
32
|
+
characters and the one syllable `wánr`; 95% is `bǎifēnzhījiǔshíwǔ` over three
|
|
33
|
+
written characters in the other order; 1988 is `yī jiǔ bā bā` over one. All
|
|
34
|
+
are annotated whole. `ConvertedPiece` gains a `source` — the characters a
|
|
35
|
+
piece reads, or undefined where it reads on into the ones named before it —
|
|
36
|
+
which is what makes that expressible.
|
|
37
|
+
|
|
38
|
+
Pinyin orthography stays out of the hanzi it annotates: no word spaces, since
|
|
39
|
+
Chinese is not written with them; the hyphen of `gāngān-jìngjìng` sits in the
|
|
40
|
+
reading, being a boundary inside one word rather than a gap between two; and
|
|
41
|
+
the base keeps 。 where the conversion writes a full stop.
|
|
42
|
+
|
|
43
|
+
- **`readings`: a conversion takes readings the caller asserts.** No rule
|
|
44
|
+
settles every polyphone, and some texts are genuinely ambiguous —
|
|
45
|
+
孩子越长越漂亮 grows where 头发越长越漂亮 lengthens, and nothing in the
|
|
46
|
+
characters says which. An application that knows its own content can now say
|
|
47
|
+
so, rather than the library guessing on its behalf.
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
convert(dictionary, "这篇文章不太长。", { readings: { 太长: "tài cháng" } });
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
A **word** hint asserts something about the text it names and rewrites exactly
|
|
54
|
+
those characters, so a bare `长` leaves 校长 as `xiàozhǎng` — the dictionary
|
|
55
|
+
knowing that word is better evidence about the stretch than a remark about one
|
|
56
|
+
of its characters, and that is what makes a corrections table safe to
|
|
57
|
+
accumulate. Naming the whole word does reach it, and keeps it whole:
|
|
58
|
+
`{ 银行: "yín xíng" }` gives `yínxíng`.
|
|
59
|
+
|
|
60
|
+
A **positional** hint asserts something about one character of one text, and
|
|
61
|
+
nothing outranks it, the enclosing word included. Positions count code points
|
|
62
|
+
from the start of the text, across any non-Han runs in it. The list form takes
|
|
63
|
+
both kinds together.
|
|
64
|
+
|
|
65
|
+
Hints enter as lattice edges rather than as rules, because the rules layer
|
|
66
|
+
cannot invent a reading no source attests — which is exactly what a hint is,
|
|
67
|
+
the difference being that a caller attests it. Spacing is untouched, an
|
|
68
|
+
unmarked syllable is 轻声, and a hint that cannot be parsed throws rather than
|
|
69
|
+
being skipped: a correction that silently does nothing is worse than one that
|
|
70
|
+
fails.
|
|
71
|
+
|
|
72
|
+
### Fixed
|
|
73
|
+
|
|
74
|
+
- **Polyphones defaulted to whichever reading a dictionary happened to print
|
|
75
|
+
first.** Unihan's `kTGHZ2013` ranked above `kMandarin`, and that field
|
|
76
|
+
prefixes every reading with its entry number in 《現代漢語規範詞典》 — 勒 is
|
|
77
|
+
`212.050:lè 212.100:lēi` — so its order is a page order, and taking the first
|
|
78
|
+
reading meant taking whichever one sorted earlier by pinyin. 殷 read `yān` and
|
|
79
|
+
楷 `jiē`. The dictionary-indexed fields now rank last: they are a good
|
|
80
|
+
_source_ of readings and a meaningless _ranking_ of them.
|
|
81
|
+
|
|
82
|
+
`kHanyuPinlu` still leads where it lists more than one reading, because there
|
|
83
|
+
it has counted them against each other, but where it lists one it counted
|
|
84
|
+
nothing — it is saying "this occurred", not "this leads" — and a lone reading
|
|
85
|
+
now ranks last too. Only ten characters have one that disagrees with
|
|
86
|
+
`kMandarin`, and eight of those are simply pre-1985: the corpus behind the
|
|
87
|
+
field, 《現代漢語頻率詞典》, predates the 普通话异读词审音表, and the 132
|
|
88
|
+
occurrences carrying 绩 as `jī` are the old standard rather than a case for
|
|
89
|
+
it.
|
|
90
|
+
|
|
91
|
+
354 readings fixed against 94 broken over 25 characters, the largest being 贾
|
|
92
|
+
`gǔ`→`jiǎ`, 苔 `tāi`→`tái`, 町 `dīng`→`tǐng`, 殷 `yān`→`yīn` and 芥
|
|
93
|
+
`gài`→`jiè`. CPP's polyphone score is 90.34%, up from 89.05%, and the gold
|
|
94
|
+
corpus does not move. What broke is mostly `kMandarin` being wrong with
|
|
95
|
+
nothing behind it — 媛 `yuán`→`yuàn`, 桔 `jú`→`jié`, 耶 `yē`→`yé`, 缪
|
|
96
|
+
`miào`→`móu` — which needs a third opinion with corpus evidence behind it
|
|
97
|
+
rather than an override, since here the sources disagree. 䘚 is now paired
|
|
98
|
+
with 卒, so `core` and `standard` each gained a key.
|
|
99
|
+
|
|
100
|
+
- **`full` read 特徵 as `tèzhǐ` where `standard` read it right.**
|
|
101
|
+
`large_pinyin.txt` is a 简体 corpus, and where it carries a 繁體 headword
|
|
102
|
+
anyway it has read it as though the characters were 简体, taking each at face
|
|
103
|
+
value instead of as the 简体 character it stands for: 徵 as itself rather than
|
|
104
|
+
as 征, 沈 as the surname rather than as 沉, so 沈溺 came out `shěnnì`. Of the
|
|
105
|
+
2,854 corpus headwords carrying a 繁體-only character, 72 are ones CC-CEDICT
|
|
106
|
+
pairs with a 简体 form and 13 of those read wrongly.
|
|
107
|
+
|
|
108
|
+
The reading was only half of it. A corpus headword becomes an entry of its
|
|
109
|
+
own, and an entry outranks the 繁體 key that the traditional derivation hangs
|
|
110
|
+
on the 简体 entry — so the word was in the dictionary twice, and which copy
|
|
111
|
+
answered depended on the tier. `standard` excludes the phrase tail and was
|
|
112
|
+
right; `full`, the default, was wrong. Loading more dictionary made the answer
|
|
113
|
+
worse, which is also the upgrade path the docs describe, of converting with
|
|
114
|
+
`standard` and reloading as `full`.
|
|
115
|
+
|
|
116
|
+
A headword CC-CEDICT knows _only_ as another word's 繁體 spelling now
|
|
117
|
+
contributes no entry, and the word keeps the reading CC-CEDICT pairs it with.
|
|
118
|
+
68 entries drop out and their keys are re-claimed from the 简体 entry, so
|
|
119
|
+
`full` loses 2 keys; 简→繁→简 round trips gain 38 against 5 lost on 繁→简→繁,
|
|
120
|
+
both unchanged as percentages. The 2,782 headwords CC-CEDICT does not pair are
|
|
121
|
+
rare or mixed-script words whose characters do read them, and are left alone.
|
|
122
|
+
Three of the words are build assertions now, this being the kind of defect
|
|
123
|
+
that comes back when a rolling source is refreshed and nothing is watching.
|
|
124
|
+
|
|
125
|
+
- **长 read `zhǎng` wherever no word covered it**, so 这篇文章不太长 came out
|
|
126
|
+
`bú tài zhǎng` and 要多长时间 `duō zhǎng shíjiān`. The stored default is not
|
|
127
|
+
the defect — Unihan ranks the readings `zhǎng(1879) cháng(1179)` and names
|
|
128
|
+
`zhǎng` in `kMandarin`, and that ranking is what reads 署长, 团长, 公安局长 and
|
|
129
|
+
总会长 correctly off a bare 长 at the end of a title. What was missing is that
|
|
130
|
+
nothing could prefer the alternate at a position no word covers.
|
|
131
|
+
|
|
132
|
+
A degree adverb settles it, and only from the left: a growing 长 is a verb and
|
|
133
|
+
no 很, 太, 最 or 多 can modify one, while what follows an adjectival 长 is a
|
|
134
|
+
noun, a particle or the end of the sentence — which is what follows half the
|
|
135
|
+
verbs too. 得, 着 and the 越…越 correlative are the exceptions, and are guarded;
|
|
136
|
+
了 and 的 are deliberately not, since after an adverb they are the sentence
|
|
137
|
+
particle and the attributive, making 时间太长了 and 很长的道路 both `cháng`.
|
|
138
|
+
|
|
139
|
+
Measured over the same 88,866 lines of Tatoeba and zh.wikipedia the other
|
|
140
|
+
edge rules were sized against, 260 长 decode as a word of their own and this
|
|
141
|
+
moves 75 to `cháng`, all 75 correctly. On CPP's 40 hand-labelled 长 the
|
|
142
|
+
character goes 85.00% to 87.50%; overall CPP is unmoved at 90.34%, as expected
|
|
143
|
+
of one character in 20,139.
|
|
144
|
+
|
|
145
|
+
- **越长越高 read `yuè cháng yuè gāo`.** 越长 is a key read `yuè cháng` and the
|
|
146
|
+
only one of its shape — 越大, 越高, 越好 and 越快 are all absent, so 越高越好
|
|
147
|
+
decodes as two words while 越长越高 reached for a word nothing else in the
|
|
148
|
+
paradigm has. It carries no part of speech, which is how a reading somebody
|
|
149
|
+
asserted is held rather than a word anybody counted, and only
|
|
150
|
+
`large_pinyin.txt` has it. Where the far half of the correlative names
|
|
151
|
+
something growing produces — 高, 大, 胖, 壮, 结实 — the edge is dropped and the
|
|
152
|
+
character's own `zhǎng` stands.
|
|
153
|
+
|
|
154
|
+
Unlike everything else here this is a heuristic with no corpus behind it: 越长
|
|
155
|
+
occurs three times in the 88,866 lines and all three are 越来越长 or 说的越长.
|
|
156
|
+
It forbids rather than forces, so `cháng` stays on as a rival one bucket
|
|
157
|
+
dearer and the syllable still reports itself uncertain — 越长越X is genuinely
|
|
158
|
+
ambiguous, 孩子越长越漂亮 growing where 头发越长越漂亮 lengthens, which is why
|
|
159
|
+
漂亮 is not in the list. 时间越长越好 and 头发越长 are untouched.
|
|
160
|
+
|
|
161
|
+
## 1.5.3
|
|
162
|
+
|
|
163
|
+
### Fixed
|
|
164
|
+
|
|
165
|
+
- **The changelog inside the package described five releases as unreleased.**
|
|
166
|
+
Every tarball from 1.1.0 to 1.5.2 shipped a `## Unreleased` section holding
|
|
167
|
+
everything since 1.0.0, so a reader who opened the copy they installed —
|
|
168
|
+
which [the API page](docs/api/#stability) points them at — had no way to tell
|
|
169
|
+
which version any of it arrived in. The entries are filed under the versions
|
|
170
|
+
that shipped them now. No code changes; this release exists to put the
|
|
171
|
+
corrected file on the registry.
|
|
172
|
+
|
|
173
|
+
## 1.5.2
|
|
174
|
+
|
|
12
175
|
### Fixed
|
|
13
176
|
|
|
14
177
|
- **Third-tone sandhi ran over the syllables rather than over the feet.** The
|
|
@@ -52,28 +215,11 @@ which a dictionary rebuild can change, and the artifact format under `data/`.
|
|
|
52
215
|
written, so 相親 keeps the `xiàngqīn` its dominant sense carries. CC-CEDICT
|
|
53
216
|
now supplies 335 deltas rather than 349.
|
|
54
217
|
|
|
55
|
-
|
|
56
|
-
Kong glyph forms on the lookup path means a key written in a form the path
|
|
57
|
-
normalises _away from_ can never be found, and the merge derived 繁體 forms
|
|
58
|
-
per word from Unihan's variant lists with no single standard behind them — so
|
|
59
|
-
it wrote 裏 where the corpus overwhelmingly writes 裡, and 上樑, 中峯 and 义藴
|
|
60
|
-
became keys nothing could reach. The merge now derives canonical glyph forms,
|
|
61
|
-
and keys a headword's canonical spelling alongside it where the phrase corpus
|
|
62
|
-
wrote one with a 繁體 variant. **No key is now unreachable**, and Hong Kong
|
|
63
|
-
spellings resolve slightly better than before: 5,499 of the 9,978 keys with a
|
|
64
|
-
distinct Hong Kong form, against 5,439.
|
|
218
|
+
## 1.5.1
|
|
65
219
|
|
|
66
|
-
|
|
67
|
-
this character" comes from CC-CEDICT entries whose two columns differ, not
|
|
68
|
-
from the phrase corpus, which is nominally 简体 but carries 峯, 藴 and 枱 of
|
|
69
|
-
its own — counting it cut the table from 50 mappings to 32 and stopped Hong
|
|
70
|
-
Kong text converting at all. And a 繁體 form differing from its 简体 one only
|
|
71
|
-
by glyph normalisation is not evidence about script, or the alias key would
|
|
72
|
-
put that entry's 简体 characters into the 繁體-only set, which is how 发
|
|
73
|
-
briefly stopped being 简体-only.
|
|
220
|
+
Tooling and test-suite work only; nothing a consumer installs changed.
|
|
74
221
|
|
|
75
|
-
|
|
76
|
-
723,149 for `full`, 97,997 for `standard`, 16,975 for `core`.
|
|
222
|
+
## 1.5.0
|
|
77
223
|
|
|
78
224
|
### Added
|
|
79
225
|
|
|
@@ -91,6 +237,10 @@ which a dictionary rebuild can change, and the artifact format under `data/`.
|
|
|
91
237
|
command line — leaves it off, for a caller who would rather declare the
|
|
92
238
|
language once on a wrapper of their own than repeat it on every syllable.
|
|
93
239
|
|
|
240
|
+
## 1.4.0
|
|
241
|
+
|
|
242
|
+
### Added
|
|
243
|
+
|
|
94
244
|
- **`pinyinjs script`,** script conversion at the command line, with `--to` for
|
|
95
245
|
the target and `--from-script` where the text is too short for detection to
|
|
96
246
|
tell. The lines carry the converted text and nothing else so it pipes;
|
|
@@ -168,6 +318,35 @@ which a dictionary rebuild can change, and the artifact format under `data/`.
|
|
|
168
318
|
before any dictionary has loaded; `pnpm build:data` re-derives them from
|
|
169
319
|
OpenCC and fails the build if the two have drifted.
|
|
170
320
|
|
|
321
|
+
### Fixed
|
|
322
|
+
|
|
323
|
+
- **240 dictionary entries could not be looked up at all.** Recognising Hong
|
|
324
|
+
Kong glyph forms on the lookup path means a key written in a form the path
|
|
325
|
+
normalises _away from_ can never be found, and the merge derived 繁體 forms
|
|
326
|
+
per word from Unihan's variant lists with no single standard behind them — so
|
|
327
|
+
it wrote 裏 where the corpus overwhelmingly writes 裡, and 上樑, 中峯 and 义藴
|
|
328
|
+
became keys nothing could reach. The merge now derives canonical glyph forms,
|
|
329
|
+
and keys a headword's canonical spelling alongside it where the phrase corpus
|
|
330
|
+
wrote one with a 繁體 variant. **No key is now unreachable**, and Hong Kong
|
|
331
|
+
spellings resolve slightly better than before: 5,499 of the 9,978 keys with a
|
|
332
|
+
distinct Hong Kong form, against 5,439.
|
|
333
|
+
|
|
334
|
+
Two things had to be got right alongside it. The evidence for "简体 writes
|
|
335
|
+
this character" comes from CC-CEDICT entries whose two columns differ, not
|
|
336
|
+
from the phrase corpus, which is nominally 简体 but carries 峯, 藴 and 枱 of
|
|
337
|
+
its own — counting it cut the table from 50 mappings to 32 and stopped Hong
|
|
338
|
+
Kong text converting at all. And a 繁體 form differing from its 简体 one only
|
|
339
|
+
by glyph normalisation is not evidence about script, or the alias key would
|
|
340
|
+
put that entry's 简体 characters into the 繁體-only set, which is how 发
|
|
341
|
+
briefly stopped being 简体-only.
|
|
342
|
+
|
|
343
|
+
`toScript` drops the pass that was papering over this. Key counts move:
|
|
344
|
+
723,149 for `full`, 97,997 for `standard`, 16,975 for `core`.
|
|
345
|
+
|
|
346
|
+
## 1.3.0
|
|
347
|
+
|
|
348
|
+
### Added
|
|
349
|
+
|
|
171
350
|
- **`slug`,** hanzi to a URL-safe slug: 我想学中文。 is
|
|
172
351
|
`wo3-xiang3-xue2-zhong1wen2`. Built on the decode rather than on a finished
|
|
173
352
|
string, which is the whole point of it — 银行 is `yin2hang2` and not
|
|
@@ -179,30 +358,20 @@ which a dictionary rebuild can change, and the artifact format under `data/`.
|
|
|
179
358
|
The options generalise it past URLs to search keys and identifiers, which the
|
|
180
359
|
[slug page](docs/slug/) tabulates. `pinyinjs slug` at the command line.
|
|
181
360
|
|
|
361
|
+
## 1.2.2
|
|
362
|
+
|
|
363
|
+
### Added
|
|
364
|
+
|
|
182
365
|
- **`TEACHING_JIAO` and `COUNTED_MEASURE`,** two more lattice rules, and
|
|
183
366
|
`QUANTITY_CHARACTERS`, the 汉字 a quantity is written with that the second of
|
|
184
367
|
them asks about. See the fixes below for what they do and what they measure.
|
|
368
|
+
|
|
185
369
|
- **`counts` on the numeral options.** Whether the number stands immediately in
|
|
186
370
|
front of something it counts, which is what makes a lone 2 两:
|
|
187
371
|
`numeralHanzi(2, { counts: true })` is `两` and `numeralHanzi(2)` is `二`.
|
|
188
372
|
Unlike `liang` it is not a variable choice — 二个 is simply wrong — but
|
|
189
373
|
nothing about the number says whether it is counting, so the caller says.
|
|
190
374
|
`convert` sets it from the character after the digits.
|
|
191
|
-
- **`SYLLABLE_TONES` and `isAttestedTone`.** Which tones each of the 424
|
|
192
|
-
syllables is actually written in, extracted from the merged dictionary and
|
|
193
|
-
held to it by a build assertion. 424 syllables in five tones would be 2,120
|
|
194
|
-
combinations and only 1,708 of them occur, so a fifth of that grid is empty.
|
|
195
|
-
- **`Syllable.originalTone`,** the tone a neutral syllable had before it was
|
|
196
|
-
reduced. Gwoyeu Romatzyh writes the neutral tone as a dot in front of the
|
|
197
|
-
syllable _in its original tonal spelling_, so 没有 méiyou is `mei.yeou`, and
|
|
198
|
-
nothing else in a pinyin syllable records what that tone was. Optional, never
|
|
199
|
-
inferred, and used by GR alone.
|
|
200
|
-
- **Two more outside sources for the transcription tables.** 148 words from
|
|
201
|
-
en.wiktionary's Mandarin pronunciation blocks, in bopomofo, Wade-Giles, Yale
|
|
202
|
-
and GR with their tones, and all 50 rows of Wikipedia's _Help:IPA/Mandarin_
|
|
203
|
-
key. The 417-row syllabary already checked was toneless, so it could say
|
|
204
|
-
nothing about tone marks, the neutral tone, 儿化 or how a word is joined,
|
|
205
|
-
which is where the four fixes below were found.
|
|
206
375
|
|
|
207
376
|
### Fixed
|
|
208
377
|
|
|
@@ -222,6 +391,7 @@ which a dictionary rebuild can change, and the artifact format under `data/`.
|
|
|
222
391
|
`jiāo`, three of them wrongly — 统一教创始人, 方法教深思 and 做到了教政分离,
|
|
223
392
|
where a nominal compound takes an object's shape. CPP's polyphone score is
|
|
224
393
|
89.05%, up from 89.04%.
|
|
394
|
+
|
|
225
395
|
- **A 量词 was swallowed by the word behind it.** 三个人 was `sān gèrén`, three
|
|
226
396
|
_personals_, because 个人 is a common noun and nothing weighed it against the
|
|
227
397
|
个 belonging to the 三 in front of it. It is now `sān gè rén`, and so are
|
|
@@ -237,6 +407,7 @@ which a dictionary rebuild can change, and the artifact format under `data/`.
|
|
|
237
407
|
and moves 53 decodes, three of them wrongly — 一批评, 这一名词 and 六七股灾,
|
|
238
408
|
where the 一 and the 六七 count nothing. Exactly one reading changes in the
|
|
239
409
|
whole corpus and it is a fix: 已经下了两天雨了 read 天雨 as `tiān yù`.
|
|
410
|
+
|
|
240
411
|
- **The Han after a number was decoded without it.** 2个人 was `liǎng gèrén`
|
|
241
412
|
while 两个人 written out was already `liǎng gè rén`, because a Han run was
|
|
242
413
|
decoded on its own and had no idea a number preceded it. A run is now decoded
|
|
@@ -245,6 +416,7 @@ which a dictionary rebuild can change, and the artifact format under `data/`.
|
|
|
245
416
|
take that context as a fourth argument; where a reading would straddle the
|
|
246
417
|
join — 1点儿事, whose 一点儿 is one `yìdiǎnr` over both sides of it — the run
|
|
247
418
|
is decoded alone, as before.
|
|
419
|
+
|
|
248
420
|
- **A lone 2 in front of a 量词 was read 二.** 我们买了2个西瓜 came out as
|
|
249
421
|
`wǒmen mǎile èr gè xīguā`, and a 2 standing immediately in front of what it
|
|
250
422
|
counts is 两: it is now `liǎng gè`, and 2岁 is `liǎng suì`, 2点 `liǎng diǎn`
|
|
@@ -255,43 +427,22 @@ which a dictionary rebuild can change, and the artifact format under `data/`.
|
|
|
255
427
|
the 第 in front of it marks (第2次 is `dì èr cì`), and 十 and 百, which follow
|
|
256
428
|
二 as 20 and 200 already do. A 2 inside a larger number is untouched: 12个 is
|
|
257
429
|
still `shí'èr gè`.
|
|
430
|
+
|
|
431
|
+
## 1.2.1
|
|
432
|
+
|
|
433
|
+
### Fixed
|
|
434
|
+
|
|
258
435
|
- **A decimal lost the grouping of its counted part.** 一共75.5元 was
|
|
259
436
|
`yígòng qī shí wǔ diǎn wǔ yuán`, with the 75 in loose syllables, and is now
|
|
260
437
|
`yígòng qīshíwǔ diǎn wǔ yuán`, the same word 一共75元 already got. Only what
|
|
261
438
|
follows the 点 is read a digit at a time, and that part is unchanged: 3.14 is
|
|
262
439
|
still `sān diǎn yī sì`, and sandhi still stops at the point.
|
|
440
|
+
|
|
263
441
|
- **A decimal point was read as a full stop.** 一共75.5元 came back as
|
|
264
442
|
`Yígòng …`, capitalised as though the text were a sentence, and a conversion
|
|
265
443
|
keeping its digits capitalised again after the point: 我有75.5个。was
|
|
266
444
|
`Wǒ yǒu75.5Gè.` A stop between two digits now ends no sentence.
|
|
267
|
-
|
|
268
|
-
is now `ㄋㄚˇㄦ`: the mark belongs to the nucleus and the suffix is not part of
|
|
269
|
-
what it marks. A mark written after the ㄦ is still read.
|
|
270
|
-
- **Wade-Giles put the tone digit on the 儿化 suffix,** so 花儿 huār was
|
|
271
|
-
`hua-êrh¹`, a first-tone 兒. It is now `hua¹-'rh`, with the digit on the
|
|
272
|
-
syllable the tone belongs to and the suffix written as the reduced `'rh` that
|
|
273
|
-
keeps it apart from 女儿 nǚ'ér's `nü³-êrh²`.
|
|
274
|
-
- **Gwoyeu Romatzyh wrote 儿化 as a plain `-l` suffix.** GR fuses it into the
|
|
275
|
-
rime instead, and the rules are now implemented as _Spelling in Gwoyeu
|
|
276
|
-
Romatzyh_ gives them: 玩儿 wánr is `wal` rather than `wanl`, 事儿 shìr is
|
|
277
|
-
`shell`, 今儿 jīnr is `jiel`. The fusion is many-to-one, so `jiel` is 今儿 and
|
|
278
|
-
鸡儿 both and 128 forms no longer round-trip, every one of them a form the
|
|
279
|
-
language does not write.
|
|
280
|
-
- **Gwoyeu Romatzyh wrote `.mhe` for a neutral 么.** The `-h-` is the first tone
|
|
281
|
-
of a sonorant initial, and a neutral syllable is in no tone at all; the basic
|
|
282
|
-
form goes behind the dot, so 什么 shénme is `shern.me`.
|
|
283
|
-
- **The IPA module said it followed _Help:IPA/Mandarin_ and did not.** It
|
|
284
|
-
follows the broader IPA column of _Comparison of Standard Chinese
|
|
285
|
-
transcription systems_, which is where its ground truth comes from; the two
|
|
286
|
-
pages differ at the medials, -ang, the empty rhyme and the diphthongs. No
|
|
287
|
-
output changed, only the claim.
|
|
288
|
-
- **`--system ipa` capitalised the IPA.** 我去银行。他姓王。 came out as
|
|
289
|
-
`Uo˨˩˦ ... Tʰa˥ ɕiŋ˥˩ Uaŋ˧˥`, with the sentence capital and the proper noun
|
|
290
|
-
carried over from the pinyin as though a transcription spelled words. IPA
|
|
291
|
-
letters are symbols rather than an alphabet, since `[T]` is not `[t]` in a
|
|
292
|
-
larger size but a symbol the IPA does not have, so the capitals are now dropped for
|
|
293
|
-
IPA and bopomofo and kept for the three romanisations. `toTranscription`
|
|
294
|
-
takes `{ capitals: false }` for callers writing their own.
|
|
445
|
+
|
|
295
446
|
- **A compound lost the zh-TW reading its own parts carry.** 垃圾分類 read
|
|
296
447
|
`lājīfēnlèi` under `zh-TW`, because CC-CEDICT marks `Taiwan pr.` on 垃圾 and
|
|
297
448
|
垃圾桶 and on no other compound, and the decoder prefers the longer word, so
|
|
@@ -303,6 +454,7 @@ which a dictionary rebuild can change, and the artifact format under `data/`.
|
|
|
303
454
|
it, and it has to be a whole word rather than a bare character, since 會's `huǐ`
|
|
304
455
|
would rewrite 三合會 as `sānhéhuǐ` and 3,743 entries would compose that way
|
|
305
456
|
against the 101 that whole words compose.
|
|
457
|
+
|
|
306
458
|
- **A character's zh-TW delta was often a choice between its 普通话 senses.**
|
|
307
459
|
从容地 read `cōngróng dì` and 他都知道 read `tā dū zhīdào` under `zh-TW`,
|
|
308
460
|
because 地 was stored with a Taiwan reading of `dì` and 都 with `dū`. Neither
|
|
@@ -315,12 +467,75 @@ which a dictionary rebuild can change, and the artifact format under `data/`.
|
|
|
315
467
|
only read off a sense matching the reading the entry settled on, which drops
|
|
316
468
|
16 more: the note on 著's chess-move sense, which reads `zhāo`, had been
|
|
317
469
|
giving the aspect particle 着 a 國語 reading of `zhuó`.
|
|
470
|
+
|
|
318
471
|
- **A locale delta overwrote a reading it was not measured against.** The delta
|
|
319
472
|
describes the entry's own 普通话 reading, so a polyphone the decode read some
|
|
320
473
|
other way, such as 得 forced to the modal `děi` rather than the particle `de`
|
|
321
474
|
its entry stores, now keeps what the decode worked out instead of being
|
|
322
475
|
rewritten from the entry.
|
|
323
476
|
|
|
477
|
+
## 1.2.0
|
|
478
|
+
|
|
479
|
+
### Fixed
|
|
480
|
+
|
|
481
|
+
- **`--system ipa` capitalised the IPA.** 我去银行。他姓王。 came out as
|
|
482
|
+
`Uo˨˩˦ ... Tʰa˥ ɕiŋ˥˩ Uaŋ˧˥`, with the sentence capital and the proper noun
|
|
483
|
+
carried over from the pinyin as though a transcription spelled words. IPA
|
|
484
|
+
letters are symbols rather than an alphabet, since `[T]` is not `[t]` in a
|
|
485
|
+
larger size but a symbol the IPA does not have, so the capitals are now dropped for
|
|
486
|
+
IPA and bopomofo and kept for the three romanisations. `toTranscription`
|
|
487
|
+
takes `{ capitals: false }` for callers writing their own.
|
|
488
|
+
|
|
489
|
+
## 1.1.0
|
|
490
|
+
|
|
491
|
+
### Added
|
|
492
|
+
|
|
493
|
+
- **`SYLLABLE_TONES` and `isAttestedTone`.** Which tones each of the 424
|
|
494
|
+
syllables is actually written in, extracted from the merged dictionary and
|
|
495
|
+
held to it by a build assertion. 424 syllables in five tones would be 2,120
|
|
496
|
+
combinations and only 1,708 of them occur, so a fifth of that grid is empty.
|
|
497
|
+
|
|
498
|
+
- **`Syllable.originalTone`,** the tone a neutral syllable had before it was
|
|
499
|
+
reduced. Gwoyeu Romatzyh writes the neutral tone as a dot in front of the
|
|
500
|
+
syllable _in its original tonal spelling_, so 没有 méiyou is `mei.yeou`, and
|
|
501
|
+
nothing else in a pinyin syllable records what that tone was. Optional, never
|
|
502
|
+
inferred, and used by GR alone.
|
|
503
|
+
|
|
504
|
+
- **Two more outside sources for the transcription tables.** 148 words from
|
|
505
|
+
en.wiktionary's Mandarin pronunciation blocks, in bopomofo, Wade-Giles, Yale
|
|
506
|
+
and GR with their tones, and all 50 rows of Wikipedia's _Help:IPA/Mandarin_
|
|
507
|
+
key. The 417-row syllabary already checked was toneless, so it could say
|
|
508
|
+
nothing about tone marks, the neutral tone, 儿化 or how a word is joined,
|
|
509
|
+
which is where the four fixes below were found.
|
|
510
|
+
|
|
511
|
+
### Fixed
|
|
512
|
+
|
|
513
|
+
- **Bopomofo wrote the tone mark after the 儿化 ㄦ.** 哪儿 nǎr was `ㄋㄚㄦˇ` and
|
|
514
|
+
is now `ㄋㄚˇㄦ`: the mark belongs to the nucleus and the suffix is not part of
|
|
515
|
+
what it marks. A mark written after the ㄦ is still read.
|
|
516
|
+
|
|
517
|
+
- **Wade-Giles put the tone digit on the 儿化 suffix,** so 花儿 huār was
|
|
518
|
+
`hua-êrh¹`, a first-tone 兒. It is now `hua¹-'rh`, with the digit on the
|
|
519
|
+
syllable the tone belongs to and the suffix written as the reduced `'rh` that
|
|
520
|
+
keeps it apart from 女儿 nǚ'ér's `nü³-êrh²`.
|
|
521
|
+
|
|
522
|
+
- **Gwoyeu Romatzyh wrote 儿化 as a plain `-l` suffix.** GR fuses it into the
|
|
523
|
+
rime instead, and the rules are now implemented as _Spelling in Gwoyeu
|
|
524
|
+
Romatzyh_ gives them: 玩儿 wánr is `wal` rather than `wanl`, 事儿 shìr is
|
|
525
|
+
`shell`, 今儿 jīnr is `jiel`. The fusion is many-to-one, so `jiel` is 今儿 and
|
|
526
|
+
鸡儿 both and 128 forms no longer round-trip, every one of them a form the
|
|
527
|
+
language does not write.
|
|
528
|
+
|
|
529
|
+
- **Gwoyeu Romatzyh wrote `.mhe` for a neutral 么.** The `-h-` is the first tone
|
|
530
|
+
of a sonorant initial, and a neutral syllable is in no tone at all; the basic
|
|
531
|
+
form goes behind the dot, so 什么 shénme is `shern.me`.
|
|
532
|
+
|
|
533
|
+
- **The IPA module said it followed _Help:IPA/Mandarin_ and did not.** It
|
|
534
|
+
follows the broader IPA column of _Comparison of Standard Chinese
|
|
535
|
+
transcription systems_, which is where its ground truth comes from; the two
|
|
536
|
+
pages differ at the medials, -ang, the empty rhyme and the diphthongs. No
|
|
537
|
+
output changed, only the claim.
|
|
538
|
+
|
|
324
539
|
### Changed
|
|
325
540
|
|
|
326
541
|
- **Reading a romanisation narrows on the tone that was written.**
|
|
@@ -333,6 +548,7 @@ which a dictionary rebuild can change, and the artifact format under `data/`.
|
|
|
333
548
|
candidates alone, since that says the tone is wrong rather than the spelling.
|
|
334
549
|
Over the phrase corpus, taking the first candidate recovers 82.66% of
|
|
335
550
|
syllables when the tone digit is written, against 79.05% without it.
|
|
551
|
+
|
|
336
552
|
- Eight Wade-Giles forms no longer round-trip, and one to three in each of the
|
|
337
553
|
other systems, because they are forms the language does not write: `lo` in the
|
|
338
554
|
four contour tones, a first-tone 兒, and a neutral 誒. Every attested
|
|
@@ -393,14 +609,14 @@ decoder internals and the build pipeline.
|
|
|
393
609
|
|
|
394
610
|
### Accuracy at 1.0.0
|
|
395
611
|
|
|
396
|
-
Over the
|
|
612
|
+
Over the 130-case gold corpus, which is committed to the repository rather than
|
|
397
613
|
to the published package, and scored by `pnpm accuracy`:
|
|
398
614
|
|
|
399
615
|
| | lattice | greedy baseline |
|
|
400
616
|
| ---------------- | --------: | --------------: |
|
|
401
|
-
| exact match | **97.
|
|
402
|
-
| reading accuracy | 99.7% |
|
|
403
|
-
| spacing (F1) | **99.8%** |
|
|
617
|
+
| exact match | **97.7%** | 86.9% |
|
|
618
|
+
| reading accuracy | 99.7% | 97.9% |
|
|
619
|
+
| spacing (F1) | **99.8%** | 95.7% |
|
|
404
620
|
|
|
405
621
|
Every figure in that table is asserted against the scorer by
|
|
406
622
|
`src/changelog.test.ts`, because a number nothing executes goes stale, which is
|
package/README.md
CHANGED
|
@@ -59,6 +59,7 @@ běijīng běijīng ㄅㄟˇ ㄐㄧㄥ pei³-ching¹ běijīng beeij
|
|
|
59
59
|
| ------------ | --------------------------------------------------- |
|
|
60
60
|
| `convert` | hanzi to pinyin |
|
|
61
61
|
| `html` | the same, as HTML |
|
|
62
|
+
| `annotate` | hanzi with its pinyin above, as ruby HTML |
|
|
62
63
|
| `slug` | hanzi to a URL-safe slug |
|
|
63
64
|
| `script` | 简体 ↔ 繁體 conversion |
|
|
64
65
|
| `explain` | each syllable, how settled it was, and what it beat |
|
|
@@ -143,9 +144,9 @@ Load the dictionary once and keep it; it is immutable and safe to share.
|
|
|
143
144
|
|
|
144
145
|
| Tier | Entries | Download (brotli) | Contains |
|
|
145
146
|
| ---------- | ------: | ----------------: | ---------------------- |
|
|
146
|
-
| `core` | 16,
|
|
147
|
-
| `standard` | 66,
|
|
148
|
-
| `full` | 461,
|
|
147
|
+
| `core` | 16,727 | 70 KB | single characters only |
|
|
148
|
+
| `standard` | 66,727 | 376 KB | the most common words |
|
|
149
|
+
| `full` | 461,555 | 2,381 KB | every word |
|
|
149
150
|
|
|
150
151
|
`full` is the default. The tiers are nested, so a page can load `standard`
|
|
151
152
|
first, convert with it, and reload as `full` arrives.
|
|
@@ -349,6 +350,45 @@ included, so write your own:
|
|
|
349
350
|
Takes any `convert` option, plus `toneClasses: false`, `markUncertain: false`
|
|
350
351
|
and `lang: false`. `toHtml(pieces, options)` renders pieces you already have.
|
|
351
352
|
|
|
353
|
+
## Annotated output: hanzi and pinyin together
|
|
354
|
+
|
|
355
|
+
`convertToHtml` writes the pinyin _instead of_ the hanzi. To keep both — a
|
|
356
|
+
learner's text, a subtitle, a dictionary entry — annotate it instead:
|
|
357
|
+
|
|
358
|
+
```ts
|
|
359
|
+
import { convertToAnnotatedHtml } from "@kensio/pinyinjs";
|
|
360
|
+
|
|
361
|
+
convertToAnnotatedHtml(dictionary, "银行");
|
|
362
|
+
// <ruby lang="zh">银<rp>(</rp><rt><span class="py-syllable py-tone-2"
|
|
363
|
+
// lang="zh-Latn-CN-pinyin">yín</span></rt><rp>)</rp></ruby>…
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
The markup is `<ruby>`, which browsers lay out natively — the reading sits
|
|
367
|
+
above the characters, and `<rp>` puts it in parentheses where it does not. Each
|
|
368
|
+
`<rt>` holds exactly what `convertToHtml` would have written, so tone colours
|
|
369
|
+
and uncertainty marking work inside an annotation as they do outside one.
|
|
370
|
+
|
|
371
|
+
**A base is not always one character**, and this is where per-character
|
|
372
|
+
annotation goes wrong. 玩儿 is two characters and the one syllable `wánr`, and a
|
|
373
|
+
read number reverses on the way, so both are annotated whole:
|
|
374
|
+
|
|
375
|
+
```ts
|
|
376
|
+
convertToAnnotatedHtml(dictionary, "玩儿"); // one <ruby>, 玩儿 over wánr
|
|
377
|
+
convertToAnnotatedHtml(dictionary, "95%"); // one <ruby>, 95% over bǎifēnzhījiǔshíwǔ
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
Ruby needs no styling to work, but the reading is small by default:
|
|
381
|
+
|
|
382
|
+
```css
|
|
383
|
+
ruby rt {
|
|
384
|
+
font-size: 0.5em;
|
|
385
|
+
}
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
`toAnnotatedHtml(pieces, options)` renders pieces you already have, and
|
|
389
|
+
`ConvertedPiece.source` is what makes that possible: the characters each piece
|
|
390
|
+
reads, or undefined where it reads on into the ones before it.
|
|
391
|
+
|
|
352
392
|
## Look words up
|
|
353
393
|
|
|
354
394
|
```ts
|
|
@@ -598,6 +638,7 @@ This is orthography and not translation — 软件 becomes 軟件, never 軟體.
|
|
|
598
638
|
| `convert(dictionary, text, options?)` | hanzi → pinyin |
|
|
599
639
|
| `convertPieces(dictionary, text, ...)` | the same, per syllable, with confidence |
|
|
600
640
|
| `convertToHtml(dictionary, text, ...)` | the same, as HTML |
|
|
641
|
+
| `convertToAnnotatedHtml(dictionary, text, ...)` | hanzi and pinyin together, as ruby HTML |
|
|
601
642
|
| `slug(dictionary, text, options?)` | hanzi → a URL-safe slug |
|
|
602
643
|
| `joinPieces(pieces)` / `toHtml(pieces)` | render pieces |
|
|
603
644
|
| `isUncertain(confidence)` | was this syllable a guess? |
|