which-dialect 0.5.0 → 0.5.1

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 CHANGED
@@ -138,7 +138,7 @@ The result has `text` (the input in Unicode NFC form), `issues` and `parts` (the
138
138
 
139
139
  **On by default (the spellchecker):**
140
140
 
141
- - **`spelling`**: a doubtful syllable is first tried with its neighbors: if fixing its accents makes a dictionary word with the syllables next to it, that word is suggested (*hom nay* → *hôm nay* "today", where *nay* alone would become *này* "this"; *an com* → *ăn cơm*; *Chung toi* → *Chúng tôi*), changing only the doubtful syllables. Otherwise, syllables that aren't in the language get the accented forms they could be (*khong* → *không*); rare plain-letter words next to a far more common accented form too (*toi* → *tôi*, *hom* → *hôm*), and, in a sentence typed without accents, any more common accented form (*an* → *ăn*, *di* → *đi*; not *con* → *còn*, about as common). Names (capitalized mid-sentence), the language's listed pronouns (*tui*) and plain-letter words with no accented form (*email*) are left alone. Languages written in syllables only (Vietnamese).
141
+ - **`spelling`**: a doubtful syllable is first tried with its neighbors: if fixing its accents makes a dictionary word with the syllables next to it, that word is suggested (*hom nay* → *hôm nay* "today", where *nay* alone would become *này* "this"; *an com* → *ăn cơm*; *Chung toi* → *Chúng tôi*), changing only the doubtful syllables. Less common words written without accents can change the same way when one dictionary word clearly wins (*mua sam* → *mua sắm* "shopping", *nha sach* → *nhà sách*), even common ones when the whole word has no accents (*nha bang* → *nhà băng* "bank"), while a common one left plain next to accented syllables is taken as meant (*tháng sau* stays), *cho con* never becomes *chó con* (not a headword) and runs that could be two words equally are left alone (*ban an*: *bản án* or *bàn ăn*). Otherwise, syllables that aren't in the language get the accented forms they could be (*khong* → *không*); rare plain-letter words next to a far more common accented form too (*toi* → *tôi*, *hom* → *hôm*), and, in a sentence typed without accents, any more common accented form (*an* → *ăn*, *di* → *đi*; not *con* → *còn*, about as common). Names (capitalized mid-sentence), the language's listed pronouns (*tui*) and plain-letter words with no accented form (*email*) are left alone. Languages written in syllables only (Vietnamese).
142
142
  - **`foreign-word`**: words in the learner's own language get the target-language word, in the region and register (*market* → *chợ*, *ice cream* → *kem*). Phrases the base dictionary knows are translated as one, falling back to single words. Words the language has no word for are suggested to be left out (`leaveOut` in its checker settings: Vietnamese has no *the*, *a*, *an*). Which language each word is in comes from the two dictionaries: an English word that's Vietnamese only with accents added (*but*: *bút*) is English; Vietnamese typed without accents (*khong*) is Vietnamese; words both languages have go by how common they are in each (*em*, *di*, *tui* are Vietnamese even between English words; *the* is English), and close calls (*an*, *to*) follow their neighbors, leaning to the target language.
143
143
 
144
144
  **Opt-in (`rules`):**
package/dist/check.js CHANGED
@@ -60,6 +60,15 @@ const ACCENTS_GAP_UNACCENTED_TEXT = 0.2;
60
60
  // combinations are looked up at most.
61
61
  const COMPOUND_FORMS_PER_SYLLABLE = 5;
62
62
  const COMPOUND_MAX_COMBINATIONS = 64;
63
+ // When none of a run's syllables is doubtful on its own (all real words written without accents, "mua sam"),
64
+ // its accented form is suggested only if one dictionary word clearly wins: ahead of the next by this many
65
+ // Zipf points ("ban an" could be "bản án" or "bàn ăn", equally common, so it's left alone).
66
+ const PLAIN_RUN_MARGIN = 0.5;
67
+ // In a run that has accents elsewhere, only syllables less common than this (Zipf) may change that way: a
68
+ // common word left without accents next to accented ones is usually meant ("tháng sau", next month, isn't
69
+ // "tháng sáu", June). A run with no accents at all ("nha bang") was probably typed without them, so any of
70
+ // its syllables may change ("nhà băng", bank), still only when one word clearly wins.
71
+ const PLAIN_RUN_MAX_ZIPF = 5;
63
72
  async function spelling(ctx) {
64
73
  if (ctx.config.units !== 'syllables' || !ctx.meta.syllables)
65
74
  return [];
@@ -94,8 +103,11 @@ async function spelling(ctx) {
94
103
  };
95
104
  const suspect = (u) => unknown.has(u) || (plainWords.has(u) && betterForms(u).length > 0);
96
105
  // First, runs of syllables with a doubtful one that make a word once their accents are fixed: "hom nay"
97
- // → "hôm nay", "an com" → "ăn cơm", "hôm nây" → "hôm nay". Only the doubtful syllables change. The word
98
- // decides between forms a syllable alone can't ("nay" alone could be này, nảy…).
106
+ // → "hôm nay", "an com" → "ăn cơm", "hôm nây" → "hôm nay". The word decides between forms a syllable
107
+ // alone can't ("nay" alone could be này, nảy…). Syllables written without accents may change too
108
+ // ("mua sam" → "mua sắm": "sam" is a word, but "mua sam" isn't, and "mua sắm" is); when none of them is
109
+ // doubtful on its own, only if one word clearly wins. Syllables written with accents never change
110
+ // unless they're doubtful.
99
111
  const covered = new Set();
100
112
  const loneSet = new Set(checked);
101
113
  const maxUnits = ctx.config.maxWordUnits ?? 3;
@@ -104,11 +116,15 @@ async function spelling(ctx) {
104
116
  const span = sentence.units.slice(i, i + k);
105
117
  if (span.some((u, j) => !loneSet.has(u) || covered.has(u) || (j > 0 && !joined(ctx.text, span[j - 1], u))))
106
118
  continue;
107
- if (!span.some(suspect))
119
+ const doubtful = span.some(suspect);
120
+ const allPlain = span.every(isPlain);
121
+ const rarePlain = (u) => plainWords.has(u) && (allPlain || (syllables[lowerOf(u)] ?? 0) < PLAIN_RUN_MAX_ZIPF);
122
+ const mayChange = (u) => suspect(u) || rarePlain(u);
123
+ if (!doubtful && !span.some(rarePlain))
108
124
  continue;
109
125
  const written = span.map(lowerOf);
110
126
  const combos = accentCombinations(written, byPlain, {
111
- fixed: span.map((u) => !suspect(u)),
127
+ fixed: span.map((u) => !mayChange(u)),
112
128
  perUnit: COMPOUND_FORMS_PER_SYLLABLE,
113
129
  max: COMPOUND_MAX_COMBINATIONS,
114
130
  }).filter((c) => c !== written.join(' '));
@@ -119,12 +135,14 @@ async function spelling(ctx) {
119
135
  .slice(0, 3);
120
136
  if (!found.length)
121
137
  continue;
138
+ if (!doubtful && found.length > 1 && found[0].frequency - found[1].frequency < PLAIN_RUN_MARGIN)
139
+ continue;
122
140
  const at = { start: span[0].start, end: span[k - 1].end, text: ctx.text.slice(span[0].start, span[k - 1].end) };
123
141
  const suggestions = found.map((f) => matchCase(at.text, f.word));
124
142
  const quoted = listOf(suggestions.map(quote), 'or');
125
143
  issues.push({
126
144
  rule: 'spelling', severity: 'warning', ...at, suggestions,
127
- message: span.filter(suspect).every(isPlain)
145
+ message: span.filter(mayChange).every(isPlain)
128
146
  ? `${quote(at.text)} looks typed without accents. Did you mean ${quoted}?`
129
147
  : `${quote(at.text)} isn't spelled right. Did you mean ${quoted}?`,
130
148
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "which-dialect",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
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",