which-dialect 0.5.5 → 0.5.6
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/README.md +4 -2
- package/dist/check.js +5 -1
- package/dist/index.js +13 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -210,7 +210,7 @@ Each sentence has `original`, `parts`, `corrected`, `changes` (`{ from, to, why,
|
|
|
210
210
|
|
|
211
211
|
One language's dictionary. `baseUrl` defaults to `https://cdn.jsdelivr.net/npm/which-dialect-<lang>@0.1/data`.
|
|
212
212
|
|
|
213
|
-
- `dictionary.lookup(word)`: all entries for a word, with every sense's definitions, regions, labels, synonyms and variant links (`altOf`).
|
|
213
|
+
- `dictionary.lookup(word)`: all entries for a word, with every sense's definitions, regions, labels, synonyms and variant links (`altOf`). The word as written, else lowercase, else capitalized, so names written in lowercase are found (*nhật* → *Nhật*, Japan; *nhật bản* → *Nhật Bản*).
|
|
214
214
|
- `dictionary.searchEnglish(term, { region?, pos?, exclude?, limit?, allSenses? })`: words for an English term in this language, one per word, best first (no meaning handling: use the translator for that). `allSenses: true` returns every matching sense of those words instead.
|
|
215
215
|
- `dictionary.frames()`: the language's sentence frames and frame words, unfilled (`{ frames, words }`; empty without frames).
|
|
216
216
|
- `dictionary.segment(text)`: the text split into the language's words, longest dictionary match first (up to the checker's `maxWordUnits`, 3 for Vietnamese): `Hôm nay tôi ăn cơm.` → *Hôm nay* · *tôi* · *ăn cơm*, each `{ text, start, end, entries }` (offsets into the NFC text; `entries` empty for words the dictionary doesn't have). Use it to treat a word of several syllables as one, e.g. to show its meaning.
|
|
@@ -301,6 +301,8 @@ npm run evaluate # translation accuracy on known-correct cas
|
|
|
301
301
|
npm run build:picks -- vi # rebuild only a language's hand-picked words (after editing `picks` in languages/vi.ts)
|
|
302
302
|
npm run picks-sheet -- vi # review sheet (.cache/picks-vi.csv) of the top English words' meanings; -- --words file.txt, -- --top 500
|
|
303
303
|
npm run picks-sheet -- vi --apply sheet.csv # config rows for the rows whose your_pick is filled in
|
|
304
|
+
npm run picks-sheet -- vi --top 300 --drafts reviews/vi-top300.json --html page.html # the same rows as a review page
|
|
305
|
+
npm run picks-sheet -- vi --apply-decisions <dir> --drafts reviews/vi-top300.json # config rows from the page's decisions
|
|
304
306
|
npm run typecheck
|
|
305
307
|
npm run build # compile the API to packages/core/dist
|
|
306
308
|
```
|
|
@@ -315,7 +317,7 @@ Generated data isn't committed (it would bloat git history); it's built before p
|
|
|
315
317
|
4. `npm run build:data -- <code>`, check the results, add a few real-data tests and evaluation cases, and publish.
|
|
316
318
|
5. Optional: [checker](#checking-text) settings (`checker` in `languages/<code>.ts`): `units: 'syllables'` for languages written in syllables (also builds the syllable list for accent suggestions), `maxWordUnits`, the reliable `pronouns` per pronoun-table column, `ambiguousPronouns`, and `politeEndings`, `similarLetters` (groups of letters often confused, for typo suggestions). Without them, the language is read as space-separated words and gets the region check.
|
|
317
319
|
6. Optional: [sentence frames](#sentence-frames): `frames` (how the language says each frame in `languages/frames.ts`) and `frameWords` (the words that change by region, each from a definition or an override with a note). Frames reach other languages through the shared catalog; a new frame goes in the catalog once.
|
|
318
|
-
7. Optional, later: [hand-picked words](#hand-picked-words). A speaker runs `npm run picks-sheet -- <code>`, fills in `your_pick` only where the first choice is wrong, and pastes the `--apply` output into `picks`.
|
|
320
|
+
7. Optional, later: [hand-picked words](#hand-picked-words). A speaker runs `npm run picks-sheet -- <code>`, fills in `your_pick` only where the first choice is wrong, and pastes the `--apply` output into `picks`. Or as a **review page**: draft suggestions for the meanings whose first word is wrong in `reviews/<code>-top300.json` (picks, confidence, why, an example sentence), build the page with `--html`, publish it as a claude.ai Artifact (it saves each decision in its database), and once a speaker has gone through it, export the decisions and turn them into config rows with `--apply-decisions`. In Claude Code, the project skill `/word-review <language> <N>` does all of this (and `/word-review <language> <N> apply` afterwards).
|
|
319
321
|
|
|
320
322
|
Languages in non-Latin scripts (Chinese, Arabic, Russian, …) will need a script-aware version of `shardKey` first: today files are split by the first two Latin letters.
|
|
321
323
|
|
package/dist/check.js
CHANGED
|
@@ -172,8 +172,12 @@ async function spelling(ctx) {
|
|
|
172
172
|
max: COMPOUND_MAX_COMBINATIONS,
|
|
173
173
|
}).filter((c) => c !== written.join(' '));
|
|
174
174
|
const found = (await Promise.all(combos.map(async (c) => ({ word: c, entries: await ctx.dict.lookup(c) }))))
|
|
175
|
+
// Names (found through a capitalized headword) only for a misspelled run: "nhat ban" → "Nhật Bản",
|
|
176
|
+
// but "hoa lan" (orchid, every syllable a word) isn't "Hòa Lan" (the Netherlands).
|
|
177
|
+
.map((f) => ({ ...f, entries: doubtful ? f.entries : f.entries.filter((e) => e.word === e.word.toLowerCase()) }))
|
|
175
178
|
.filter((f) => f.entries.length)
|
|
176
|
-
|
|
179
|
+
// Spelled as the headword: names keep their capitals ("nhat ban" → "Nhật Bản").
|
|
180
|
+
.map((f) => ({ word: f.entries[0].word.toLowerCase() === f.word ? f.entries[0].word : f.word, frequency: Math.max(-1, ...f.entries.map((e) => e.frequency ?? -1)) }))
|
|
177
181
|
.sort((a, b) => b.frequency - a.frequency)
|
|
178
182
|
.slice(0, 3);
|
|
179
183
|
if (!found.length)
|
package/dist/index.js
CHANGED
|
@@ -72,7 +72,10 @@ export function createDictionary(options) {
|
|
|
72
72
|
async function lookup(word) {
|
|
73
73
|
const w = word.trim();
|
|
74
74
|
const [m, data] = await Promise.all([meta(), shard('words', w)]);
|
|
75
|
-
|
|
75
|
+
// The word as written, else lowercase ("Mẹ" → "mẹ"), else capitalized for names written in lowercase
|
|
76
|
+
// ("nhật" → "Nhật", Japan; "nhật bản" → "Nhật Bản"), which spellchecker suggestions and learners do.
|
|
77
|
+
const titled = w.toLowerCase().replace(/(^|[\s-])(\p{Ll})/gu, (_, sep, c) => sep + c.toUpperCase());
|
|
78
|
+
const stored = data?.[w] ?? data?.[w.toLowerCase()] ?? data?.[titled] ?? [];
|
|
76
79
|
return stored.map((e) => ({ ...e, senses: e.senses.map((s) => fromStored(s, m.regions)) }));
|
|
77
80
|
}
|
|
78
81
|
const checker = async () => {
|
|
@@ -107,11 +110,15 @@ export function createDictionary(options) {
|
|
|
107
110
|
// Each form as written in lowercase and capitalized, for names ("nhat" → "Nhật", Japan; "nhat ban" →
|
|
108
111
|
// "Nhật Bản").
|
|
109
112
|
const forms = [...new Set(combos.flatMap((c) => [c, c.replace(/(^|\s)(\S)/gu, (_, sep, ch) => sep + ch.toUpperCase())]))];
|
|
110
|
-
//
|
|
111
|
-
// name (a place, a people), so given names don't
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
113
|
+
// Each form counts only as its own headword (lookup also finds other cases: "nhật" finds "Nhật"), and a
|
|
114
|
+
// capitalized one only when it's more than a person's name (a place, a people), so given names don't
|
|
115
|
+
// crowd out words.
|
|
116
|
+
const found = (await Promise.all(forms.map(async (c) => ({ word: c, entries: await lookup(c) })))).map((v) => ({
|
|
117
|
+
...v,
|
|
118
|
+
entries: v.entries.filter((e) => v.word === v.word.toLowerCase()
|
|
119
|
+
? !(e.word !== v.word && e.word.toLowerCase() === v.word)
|
|
120
|
+
: e.word === v.word && e.senses.some((s) => !PERSONAL_NAME.test(s.glosses[0] ?? ''))),
|
|
121
|
+
}));
|
|
115
122
|
const frequency = (v) => {
|
|
116
123
|
const known = v.entries.map((e) => e.frequency).filter((f) => f !== undefined);
|
|
117
124
|
return known.length ? Math.max(...known) : parts.length === 1 ? list[v.word.toLowerCase()] || undefined : undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "which-dialect",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.6",
|
|
4
4
|
"description": "Translate words between languages and dialects, meaning by meaning, starting with Vietnamese (Northern, Central, Southern) and English, with Wiktionary data loaded on demand. More languages are planned.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|