which-dialect 0.5.1 → 0.5.2
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 +1 -1
- package/dist/detect.js +37 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -139,7 +139,7 @@ The result has `text` (the input in Unicode NFC form), `issues` and `parts` (the
|
|
|
139
139
|
**On by default (the spellchecker):**
|
|
140
140
|
|
|
141
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
|
-
- **`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
|
|
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 also Vietnamese once accents are added is weighed by how common each is (*rat* is *rất*, far more common than English *rat*; *but* stays English, far more common than *bút*), and close calls (*met*: *mệt* or English *met*) are Vietnamese only among Vietnamese words; 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`):**
|
|
145
145
|
|
package/dist/detect.js
CHANGED
|
@@ -7,15 +7,21 @@ import { plain } from "./text.js";
|
|
|
7
7
|
* in each: clearly more common in the target, the target's (Vietnamese "em", "di" next to English
|
|
8
8
|
* "hello", "shopping": English has rare "em", "di"); far more common in the base, the base's ("the");
|
|
9
9
|
* otherwise, and for units neither knows (names), the nearest decided neighbors' language when they agree,
|
|
10
|
-
* else the target's (the text is mainly the target language: "an" next to English "the"). In languages
|
|
11
|
-
* word that's a target
|
|
12
|
-
*
|
|
10
|
+
* else the target's (the text is mainly the target language: "an" next to English "the"). In languages
|
|
11
|
+
* written in syllables, a base word that's a target word only once accents are added is weighed the same
|
|
12
|
+
* way, using its accented form's frequency: "rat" is "rất" (far more common than English "rat"); close
|
|
13
|
+
* calls ("met": mệt or English met) go to the target only when their neighbors are the target's ("Tôi met
|
|
14
|
+
* quá"), so English stays English among English ("kem but it was": but, not bút). A word only the target
|
|
15
|
+
* knows without its accents ("khong") is still the target's. The base is known through the translator, so
|
|
13
16
|
* inflected forms count ("went", "cooked").
|
|
14
17
|
*/
|
|
15
18
|
// Zipf points by which a word both languages know must be more common in one to count as that language's
|
|
16
19
|
// whatever its neighbors: less for the target, since the text is mainly the target language.
|
|
17
20
|
const TARGET_MARGIN = 1;
|
|
18
21
|
const BASE_MARGIN = 2;
|
|
22
|
+
// For a base word that's a target word only with accents added, a smaller lead keeps it the base's: it was
|
|
23
|
+
// typed exactly as the base word ("but" is 1.9 more common in English than "bút" in Vietnamese).
|
|
24
|
+
const BASE_MARGIN_ACCENTS = 1;
|
|
19
25
|
export async function markLanguages(list, o) {
|
|
20
26
|
const syllables = await o.target.syllables();
|
|
21
27
|
const syllableLanguage = Object.keys(syllables).length > 0;
|
|
@@ -37,9 +43,18 @@ export async function markLanguages(list, o) {
|
|
|
37
43
|
if (t === 'exact')
|
|
38
44
|
return b ? 'both' : o.lang;
|
|
39
45
|
if (b)
|
|
40
|
-
return o.base;
|
|
46
|
+
return t === 'accents' ? 'accents' : o.base;
|
|
41
47
|
return t === 'accents' ? o.lang : '?';
|
|
42
48
|
}));
|
|
49
|
+
// The most common form a plain-letter unit takes once accents are added ("rat" → rất, 6.4).
|
|
50
|
+
const accentedFrequency = (u) => {
|
|
51
|
+
const bare = plain(u.text.toLowerCase());
|
|
52
|
+
let best = 0;
|
|
53
|
+
for (const [s, f] of Object.entries(syllables))
|
|
54
|
+
if (f > best && plain(s) === bare)
|
|
55
|
+
best = f;
|
|
56
|
+
return best;
|
|
57
|
+
};
|
|
43
58
|
// How common a word is in each language (Zipf; 0 when unknown).
|
|
44
59
|
const targetFrequency = async (u) => {
|
|
45
60
|
const lower = u.text.toLowerCase();
|
|
@@ -48,18 +63,29 @@ export async function markLanguages(list, o) {
|
|
|
48
63
|
return (await o.target.lookup(lower))[0]?.frequency ?? 0;
|
|
49
64
|
};
|
|
50
65
|
const baseFrequency = async (u) => (await o.baseDictionary.lookup(u.text.toLowerCase()))[0]?.frequency ?? 0;
|
|
51
|
-
// Words both languages know, decided by frequency when it's clear.
|
|
66
|
+
// Words both languages know, decided by frequency when it's clear. Base words that are target words with
|
|
67
|
+
// accents added use that form's frequency, and close calls are marked '?accents' (decided by neighbors,
|
|
68
|
+
// more strictly).
|
|
52
69
|
const decided = await Promise.all(marks.map(async (m, i) => {
|
|
53
|
-
if (m !== 'both')
|
|
70
|
+
if (m !== 'both' && m !== 'accents')
|
|
54
71
|
return m;
|
|
55
|
-
const [t, b] = await Promise.all([targetFrequency(list[i]), baseFrequency(list[i])]);
|
|
56
|
-
|
|
72
|
+
const [t, b] = await Promise.all([m === 'accents' ? accentedFrequency(list[i]) : targetFrequency(list[i]), baseFrequency(list[i])]);
|
|
73
|
+
if (t - b >= TARGET_MARGIN)
|
|
74
|
+
return o.lang;
|
|
75
|
+
if (b - t >= (m === 'accents' ? BASE_MARGIN_ACCENTS : BASE_MARGIN))
|
|
76
|
+
return o.base;
|
|
77
|
+
return m === 'accents' ? '?accents' : '?';
|
|
57
78
|
}));
|
|
79
|
+
const known = (x) => x !== '?' && x !== '?accents';
|
|
58
80
|
return decided.map((m, i) => {
|
|
59
|
-
if (m
|
|
81
|
+
if (known(m))
|
|
60
82
|
return m;
|
|
61
|
-
const left = decided.slice(0, i).reverse().find(
|
|
62
|
-
const right = decided.slice(i + 1).find(
|
|
83
|
+
const left = decided.slice(0, i).reverse().find(known);
|
|
84
|
+
const right = decided.slice(i + 1).find(known);
|
|
85
|
+
// A word written as typed in the base language needs every decided neighbor to be the target's to be
|
|
86
|
+
// read as the target's without its accents ("Tôi met quá": mệt); otherwise it's the base's.
|
|
87
|
+
if (m === '?accents')
|
|
88
|
+
return (left ?? o.lang) === o.lang && (right ?? o.lang) === o.lang && (left || right) ? o.lang : o.base;
|
|
63
89
|
return left && right && left !== right ? o.lang : left ?? right ?? o.lang;
|
|
64
90
|
});
|
|
65
91
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "which-dialect",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2",
|
|
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",
|