prpm-dbp 0.13.2 → 0.15.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/README.md +3 -1
- package/SKILL.md +32 -0
- package/bin/cli.js +30 -0
- package/package.json +1 -1
- package/src/prpm.js +52 -3
- package/src/semak.js +104 -0
- package/src/tukar.js +26 -4
package/README.md
CHANGED
|
@@ -132,7 +132,9 @@ Pengguna kemudian menarik versi baharu dengan `npx skills update`.
|
|
|
132
132
|
| `banding <kata...>` | silang semak PRPM lawan KamusDBP |
|
|
133
133
|
| `cache` | status cache, `--buang <kata>` untuk padam satu |
|
|
134
134
|
|
|
135
|
-
Pilihan: `--refresh`, `--had <n>`, `--delay <ms>`, `--json`, `--cepat`, `--pedoman`.
|
|
135
|
+
Pilihan: `--refresh`, `--had <n>`, `--delay <ms>`, `--json`, `--cepat`, `--pedoman`, `--luar-talian`, `--homograf`, `--konteks`.
|
|
136
|
+
|
|
137
|
+
Merumikan ialah keputusan peringkat ayat, bukan peringkat perkataan. `مڠيکوت` ialah ejaan sah bagi `mengikut` dan `mengekot` sekali gus, kedua-duanya entri PRPM sebenar. Gunakan `--konteks` supaya ayat dan makna setiap calon dipaparkan bersama, berserta bilangan rujukan korpus DBP yang mengisih calon lazim ke hadapan.
|
|
136
138
|
|
|
137
139
|
`--luar-talian` tidak pernah menyentuh rangkaian. Wajib untuk laluan permintaan aplikasi: dokumen penuh dari cache mengambil 0.19 saat, manakala satu perkataan baharu daripada PRPM mengambil beberapa saat.
|
|
138
140
|
|
package/SKILL.md
CHANGED
|
@@ -13,6 +13,23 @@ Model boleh hasilkan Jawi yang nampak betul tetapi salah, dan pembaca tidak dapa
|
|
|
13
13
|
|
|
14
14
|
0. **Semak ejaan ikut DBP, bukan ikut ingatan.** Ini peraturan pertama kerana ia yang paling kerap dilanggar. Perkataan yang anda "tahu" ejaannya masih perlu disemak. Contoh sebenar daripada penggunaan: `sunah` ialah `سنة` tetapi `sunat` ialah `سونت`; `bena` langsung tiada ejaan Jawi dalam PRPM walaupun ia perkataan yang sah; `salat` hanyalah rujukan silang kepada `solat`. Tiada satu pun daripada perbezaan ini boleh diteka daripada bentuk Rumi. Jalankan `lookup` atau `jawi`, jangan tulis daripada ingatan.
|
|
15
15
|
|
|
16
|
+
0b. **Dua langkah untuk menentukan sesuatu perkataan betul.** Pertama, pastikan ia **ada dalam PRPM**. Kedua, semak **konteks ayat**. Langkah kedua tidak boleh dilangkau: ejaan Jawi menggunakan empat huruf vokal berbanding enam dalam Rumi, jadi `مڠيکوت` ialah ejaan sah bagi `mengikut` dan `mengekot` sekali gus. Kamus boleh mengesahkan perkataan itu wujud; hanya ayat boleh menentukan yang mana satu dimaksudkan.
|
|
17
|
+
|
|
18
|
+
0c. **PRPM sendiri boleh tersilap.** Entri `menyebuk` membawa sebutan dan definisi yang betul tetapi ejaan Jawi `مڽبوت`, iaitu ejaan `menyebut`. Pakej menandakannya dalam medan `mencurigakan` apabila huruf akhir Jawi tidak sepadan dengan hujung Rumi. Tandaan itu bermakna semak sendiri di PRPM, bukan buang.
|
|
19
|
+
|
|
20
|
+
0d. **Merumikan ialah keputusan peringkat AYAT, bukan peringkat perkataan.** Jangan tukar token demi token dan anggap ia selesai. Ejaan Jawi menggunakan empat huruf vokal berbanding enam dalam Rumi, jadi satu ejaan kerap membawa lebih daripada satu bacaan sah. `مڠيکوت` ialah `mengikut` **dan** `mengekot`, dua-duanya entri PRPM sebenar dengan sebutan sendiri. Kamus mengesahkan kedua-duanya wujud; hanya ayat menentukan yang mana dimaksudkan.
|
|
21
|
+
|
|
22
|
+
Gunakan `--konteks` supaya ayat dan makna setiap calon dipaparkan bersama:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
مڠيکوت -> mengikut atau mengekot
|
|
26
|
+
ayat: itu mengikut|mengekot sukatan pelajaran.
|
|
27
|
+
mengikut 2439 rujukan pergi bersama-sama dgn, menurut, mengiring
|
|
28
|
+
mengekot 6 rujukan merapatkan paha dan tangan ke badan
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Bilangan rujukan itu datang daripada panel korpus DBP sendiri, bukan anggaran. Ia **mengisih** calon supaya yang lazim dilihat dahulu; ia **tidak memilih**. Perkataan jarang tetap betul apabila ayat menghendakinya.
|
|
32
|
+
|
|
16
33
|
1. **Jangan hantar Jawi yang belum disemak.** Setiap perkataan Jawi dalam output mesti datang dari PRPM atau ditanda belum disahkan.
|
|
17
34
|
2. **Tanda, jangan sekat.** Kalau satu perkataan tiada dalam PRPM, hantar kerja itu dan senaraikan perkataan berkenaan di hujung. Jangan tahan seluruh dokumen sebab satu nama khas.
|
|
18
35
|
3. **Sebut sumber.** PRPM ada beberapa kamus. Bila petik definisi, sebut kamus mana (Kamus Dewan Edisi Keempat, Kamus Pelajar Edisi Kedua, dan lain-lain).
|
|
@@ -308,6 +325,21 @@ $ npx prpm-dbp jawi nota.txt --homograf
|
|
|
308
325
|
homograf, ejaan Jawi dikongsi (3): اݢام=agama/igama, ساتو=satu/sato, بوروڠ=burung/borong
|
|
309
326
|
```
|
|
310
327
|
|
|
328
|
+
## Laporan check-glosari
|
|
329
|
+
|
|
330
|
+
Empat kelas berasingan, kerana menggabungkannya menenggelamkan yang penting:
|
|
331
|
+
|
|
332
|
+
| Medan | Maksud |
|
|
333
|
+
|---|---|
|
|
334
|
+
| `bercanggah` | ejaan benar-benar berbeza daripada PRPM |
|
|
335
|
+
| `ejaanLama` | berbeza **hanya** pada pasangan ortografi lama/baharu yang boleh dipetakan: `ف`→`ڤ`, `ج`→`چ`, `ة`→`ه`, `ك`→`ک` |
|
|
336
|
+
| `homograf` | dua kata Rumi dalam glosari berkongsi ejaan Jawi yang sama |
|
|
337
|
+
| `mencurigakan` | glosari sepadan dengan PRPM, tetapi huruf akhir PRPM sendiri tidak munasabah |
|
|
338
|
+
|
|
339
|
+
`homograf` ialah satu-satunya kelas yang lolos senyap daripada semakan pasangan biasa: glosari boleh sepadan dengan PRPM dan **tetap salah** kerana ejaan itu dikongsi. `اية` sepadan dengan `ayah` dalam DBP, tetapi dalam teks al-Quran ia `آية` bermaksud `ayat`. Menerima DBP membuta di situ menghasilkan "menyatakan takrif **ayah** 1-5 Surah Al-Baqarah".
|
|
340
|
+
|
|
341
|
+
Penambahan huruf vokal **tidak** dikira ejaan lama. Kalau kehadiran `ي` diabaikan, `بينا` (bina) dan `بنا` (bena) akan kelihatan sama, sedangkan itu dua perkataan berlainan.
|
|
342
|
+
|
|
311
343
|
## Rantai kepercayaan
|
|
312
344
|
|
|
313
345
|
Setiap ejaan Jawi yang dikeluarkan membawa asal-usulnya. Jangan runtuhkan lapisan ini.
|
package/bin/cli.js
CHANGED
|
@@ -37,6 +37,7 @@ Pilihan: --refresh --had <n> --delay <ms> --json --cepat
|
|
|
37
37
|
--pedoman jana imbuhan ikut peraturan DBP (88-98% tepat, hasil ditanda)
|
|
38
38
|
--luar-talian jangan sentuh rangkaian langsung (wajib untuk laluan permintaan app)
|
|
39
39
|
--homograf tandakan perkataan yang ejaan Jawinya dikongsi perkataan lain
|
|
40
|
+
--konteks papar ayat dan makna setiap calon bagi kekaburan (rumi)
|
|
40
41
|
|
|
41
42
|
Cache: ${CACHE_PATH}
|
|
42
43
|
`
|
|
@@ -53,6 +54,7 @@ function huraiArgs(argv) {
|
|
|
53
54
|
else if (a === '--pedoman') pilihan.pedoman = true
|
|
54
55
|
else if (a === '--luar-talian') pilihan.luarTalian = true
|
|
55
56
|
else if (a === '--homograf') pilihan.homograf = true
|
|
57
|
+
else if (a === '--konteks') pilihan.konteks = true
|
|
56
58
|
else if (a === '--jenis') pilihan.jenis = argv[++i]
|
|
57
59
|
else if (a === '--had') pilihan.had = Number(argv[++i])
|
|
58
60
|
else if (a === '--delay') pilihan.delay = Number(argv[++i])
|
|
@@ -186,6 +188,12 @@ async function main() {
|
|
|
186
188
|
h.dijanaPedoman.map((x) => `${x.kata}=${x.jawi} [${x.rujukan}]`).join(', '),
|
|
187
189
|
)
|
|
188
190
|
}
|
|
191
|
+
if (h.mencurigakan?.length) {
|
|
192
|
+
console.error(
|
|
193
|
+
`ejaan PRPM mencurigakan (${h.mencurigakan.length}): ` +
|
|
194
|
+
h.mencurigakan.map((x) => `${x.kata}=${x.jawi} (hujung ${x.hujungRumi} vs ${x.hujungJawi})`).join(', '),
|
|
195
|
+
)
|
|
196
|
+
}
|
|
189
197
|
if (h.homograf?.length) {
|
|
190
198
|
console.error(
|
|
191
199
|
`homograf, ejaan Jawi dikongsi (${h.homograf.length}): ` +
|
|
@@ -219,6 +227,28 @@ async function main() {
|
|
|
219
227
|
if (h.tidakDikenali.length) {
|
|
220
228
|
console.error(`tidak dikenali (${h.tidakDikenali.length}): ${h.tidakDikenali.join(', ')}`)
|
|
221
229
|
}
|
|
230
|
+
if (pilihan.konteks && h.kabur.length) {
|
|
231
|
+
// Kekaburan hanya boleh diselesaikan oleh ayat, bukan oleh kamus.
|
|
232
|
+
// Papar ayat penuh berserta makna setiap calon supaya keputusan
|
|
233
|
+
// itu boleh dibuat tanpa membuka PRPM satu per satu.
|
|
234
|
+
const ayat = h.teks.split(/(?<=[.!?])\s+/)
|
|
235
|
+
console.error('\n--- konteks untuk kekaburan ---')
|
|
236
|
+
for (const k of h.kabur) {
|
|
237
|
+
const pilihanTeks = k.calon.join('|')
|
|
238
|
+
const induk = ayat.find((a) => a.includes(pilihanTeks)) ?? ''
|
|
239
|
+
console.error(`\n${k.jawi} -> ${k.calon.join(' atau ')}`)
|
|
240
|
+
if (induk) console.error(` ayat: ${induk.trim()}`)
|
|
241
|
+
for (const c of k.calon) {
|
|
242
|
+
const rec = cache[c]
|
|
243
|
+
const def = (rec?.entri?.[0]?.definisi ?? '').slice(0, 70)
|
|
244
|
+
const kerap = rec?.rujukanKorpus
|
|
245
|
+
console.error(
|
|
246
|
+
` ${c.padEnd(12)} ${kerap != null ? String(kerap).padStart(5) + ' rujukan' : ' kekerapan tidak diketahui'}` +
|
|
247
|
+
(def ? ` ${def}` : ''),
|
|
248
|
+
)
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
222
252
|
if (h.arab.length) console.error(`teks Arab dibiarkan (${h.arab.length}): ${h.arab.join(' ')}`)
|
|
223
253
|
for (const k of h.kabur) console.error(`kabur: ${k.jawi} -> ${k.calon.join(', ')}`)
|
|
224
254
|
break
|
package/package.json
CHANGED
package/src/prpm.js
CHANGED
|
@@ -34,6 +34,15 @@ const DEFINISI = /<b>Definisi\s*:\s*<\/b>([\s\S]*?)(?=<b>\(|$)/
|
|
|
34
34
|
const SUMBER = /<b>\(([^)]+)\)<\/b>/
|
|
35
35
|
const TIADA_TESAURUS = /Tiada maklumat tesaurus untuk kata/
|
|
36
36
|
|
|
37
|
+
// Panel "Juga ditemukan dalam" menyenaraikan bilangan rujukan perkataan itu
|
|
38
|
+
// dalam setiap korpus DBP. Jumlahnya ialah isyarat kekerapan yang BERSUMBER,
|
|
39
|
+
// bukan tekaan: mengikut 2439 rujukan berbanding mengekot 6.
|
|
40
|
+
//
|
|
41
|
+
// Digunakan untuk MENGISIH calon yang kabur, bukan untuk memilih. Perkataan
|
|
42
|
+
// jarang tetap betul apabila ayat menghendakinya.
|
|
43
|
+
const PANEL_KORPUS = /Juga ditemukan dalam:([\s\S]*?)Perkhidmatan/
|
|
44
|
+
const KIRAAN_KORPUS = /<i>\((\d+)\)<\/i>/g
|
|
45
|
+
|
|
37
46
|
const ENTITIES = { amp: '&', lt: '<', gt: '>', quot: '"', '#39': "'", nbsp: ' ' }
|
|
38
47
|
|
|
39
48
|
export function stripTags(raw) {
|
|
@@ -65,11 +74,17 @@ export function parse(kata, page) {
|
|
|
65
74
|
})
|
|
66
75
|
}
|
|
67
76
|
|
|
77
|
+
const panel = page.match(PANEL_KORPUS)
|
|
78
|
+
const rujukanKorpus = panel
|
|
79
|
+
? [...panel[1].matchAll(KIRAAN_KORPUS)].reduce((jum, m) => jum + Number(m[1]), 0)
|
|
80
|
+
: null
|
|
81
|
+
|
|
68
82
|
const semuaJawi = entri.map((e) => e.jawi).filter(Boolean)
|
|
69
83
|
const unik = [...new Set(semuaJawi)]
|
|
70
84
|
return {
|
|
71
85
|
kata,
|
|
72
86
|
dijumpai: entri.length > 0,
|
|
87
|
+
rujukanKorpus,
|
|
73
88
|
jawi: unik[0] ?? null,
|
|
74
89
|
jawiVarian: unik.slice(1),
|
|
75
90
|
entri,
|
|
@@ -252,18 +267,52 @@ export async function auditJawi(jawiTeks, rumiTeks, cache, opts = {}) {
|
|
|
252
267
|
|
|
253
268
|
/** Bandingkan glosari rumi->jawi sedia ada dengan ejaan rasmi PRPM. */
|
|
254
269
|
export async function semakGlosari(glosari, cache, opts = {}) {
|
|
270
|
+
const { ejaanLamaSahaja, semakHurufAkhir } = await import('./semak.js')
|
|
255
271
|
const bercanggah = []
|
|
272
|
+
const ejaanLama = []
|
|
273
|
+
const mencurigakan = []
|
|
256
274
|
const tiadaDalamPrpm = []
|
|
257
275
|
let sepadan = 0
|
|
258
276
|
const senarai = Object.entries(glosari)
|
|
259
277
|
|
|
278
|
+
// Homograf ialah satu-satunya kelas ralat yang lolos senyap daripada
|
|
279
|
+
// semakan pasangan: glosari boleh sepadan dengan PRPM dan tetap salah
|
|
280
|
+
// kerana ejaan itu dikongsi perkataan lain. اية sepadan dengan `ayah`
|
|
281
|
+
// dalam DBP, tetapi dalam teks al-Quran ia آية bermaksud `ayat`.
|
|
282
|
+
const ikutJawi = new Map()
|
|
283
|
+
for (const [rumi, jawi] of senarai) {
|
|
284
|
+
if (!ikutJawi.has(jawi)) ikutJawi.set(jawi, [])
|
|
285
|
+
ikutJawi.get(jawi).push(rumi)
|
|
286
|
+
}
|
|
287
|
+
|
|
260
288
|
for (const [i, [rumi, jawi]] of senarai.entries()) {
|
|
261
289
|
const rec = await lookup(rumi, cache, opts)
|
|
262
290
|
if (!rec.jawi) tiadaDalamPrpm.push(rumi)
|
|
263
|
-
else if (rec.jawi === jawi || (rec.jawiVarian ?? []).includes(jawi))
|
|
264
|
-
|
|
291
|
+
else if (rec.jawi === jawi || (rec.jawiVarian ?? []).includes(jawi)) {
|
|
292
|
+
sepadan++
|
|
293
|
+
const curiga = semakHurufAkhir(rumi, rec.jawi)
|
|
294
|
+
if (curiga) mencurigakan.push(curiga)
|
|
295
|
+
} else if (ejaanLamaSahaja(jawi, rec.jawi)) {
|
|
296
|
+
// Ejaan lama boleh dipetakan kepada ejaan baharu. Bukan salah,
|
|
297
|
+
// cuma ortografi yang berbeza zaman.
|
|
298
|
+
ejaanLama.push({ rumi, dalamGlosari: jawi, prpm: rec.jawi, url: rec.url })
|
|
299
|
+
} else {
|
|
300
|
+
bercanggah.push({ rumi, dalamGlosari: jawi, prpm: rec.jawi, url: rec.url })
|
|
301
|
+
}
|
|
265
302
|
opts.onProgress?.(i + 1, senarai.length)
|
|
266
303
|
}
|
|
267
304
|
|
|
268
|
-
|
|
305
|
+
const homograf = [...ikutJawi]
|
|
306
|
+
.filter(([, kata]) => kata.length > 1)
|
|
307
|
+
.map(([jawi, kata]) => ({ jawi, dikongsi: kata }))
|
|
308
|
+
|
|
309
|
+
return {
|
|
310
|
+
jumlah: senarai.length,
|
|
311
|
+
sepadan,
|
|
312
|
+
bercanggah,
|
|
313
|
+
ejaanLama,
|
|
314
|
+
homograf,
|
|
315
|
+
mencurigakan,
|
|
316
|
+
tiadaDalamPrpm,
|
|
317
|
+
}
|
|
269
318
|
}
|
package/src/semak.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Semakan kewarasan ejaan: adakah huruf akhir Jawi sepadan dengan hujung Rumi?
|
|
3
|
+
*
|
|
4
|
+
* Diperlukan kerana PRPM sendiri kadangkala menyimpan ejaan yang salah pada
|
|
5
|
+
* kata kepala. Contoh sebenar: entri `menyebuk` membawa sebutan [me.nye.buʔ]
|
|
6
|
+
* dan definisi "masuk mencelah" yang betul, tetapi ejaan Jawi مڽبوت, iaitu
|
|
7
|
+
* ejaan `menyebut`. Huruf akhir ت bermaksud "t", sedangkan menyebuk berakhir
|
|
8
|
+
* dengan "k". Ekstraksi kita setia; sumbernya yang rosak.
|
|
9
|
+
*
|
|
10
|
+
* Diukur terhadap 37,801 pasangan sebenar: 99.6% sepadan. Yang tidak sepadan
|
|
11
|
+
* hampir kesemuanya kata serapan Arab yang mengekalkan ortografi asal
|
|
12
|
+
* (bahas بحث, asah اصح), jadi huruf Arab disenaraikan sebagai padanan sah.
|
|
13
|
+
*
|
|
14
|
+
* Ini menandakan, bukan menolak. Ejaan yang mencurigakan tetap dipulangkan
|
|
15
|
+
* supaya pengguna melihatnya dan boleh menyemak sendiri di PRPM.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
// Padanan huruf akhir Rumi kepada huruf akhir Jawi yang boleh diterima.
|
|
19
|
+
// Varian Arab disertakan kerana §11.1 mengekalkan ejaan asal bagi istilah
|
|
20
|
+
// khusus: bahas بحث guna ث untuk s, asah اصح guna ح untuk h.
|
|
21
|
+
const PADANAN_AKHIR = new Map([
|
|
22
|
+
['t', ['ت', 'ة', 'ط']],
|
|
23
|
+
['n', ['ن']],
|
|
24
|
+
['m', ['م']],
|
|
25
|
+
['h', ['ه', 'ة', 'ح']],
|
|
26
|
+
['r', ['ر']],
|
|
27
|
+
['l', ['ل']],
|
|
28
|
+
['s', ['س', 'ص', 'ث']],
|
|
29
|
+
['p', ['ڤ', 'ف']],
|
|
30
|
+
['g', ['ݢ', 'ڬ']],
|
|
31
|
+
['k', ['ق', 'ک', 'ك', 'ء']],
|
|
32
|
+
['d', ['د', 'ض']],
|
|
33
|
+
['b', ['ب']],
|
|
34
|
+
['f', ['ف']],
|
|
35
|
+
['z', ['ز', 'ظ', 'ذ']],
|
|
36
|
+
])
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Pulangkan null jika waras atau tidak dapat disemak; pulangkan butiran
|
|
40
|
+
* jika huruf akhir tidak sepadan.
|
|
41
|
+
*/
|
|
42
|
+
export function semakHurufAkhir(rumi, jawi) {
|
|
43
|
+
if (!rumi || !jawi) return null
|
|
44
|
+
const kata = String(rumi).trim().toLowerCase()
|
|
45
|
+
const ejaan = String(jawi).trim()
|
|
46
|
+
if (kata.includes('-') || kata.length < 3) return null
|
|
47
|
+
|
|
48
|
+
// -ng dan -ny ialah digraf; huruf akhirnya bukan penunjuk yang sah.
|
|
49
|
+
if (kata.endsWith('ng') || kata.endsWith('ny')) return null
|
|
50
|
+
|
|
51
|
+
const jangkaan = PADANAN_AKHIR.get(kata.slice(-1))
|
|
52
|
+
if (!jangkaan) return null
|
|
53
|
+
|
|
54
|
+
const akhirJawi = ejaan.slice(-1)
|
|
55
|
+
if (jangkaan.includes(akhirJawi)) return null
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
kata,
|
|
59
|
+
jawi: ejaan,
|
|
60
|
+
hujungRumi: kata.slice(-1),
|
|
61
|
+
hujungJawi: akhirJawi,
|
|
62
|
+
dijangka: jangkaan,
|
|
63
|
+
sebab: 'huruf akhir Jawi tidak sepadan dengan hujung Rumi',
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Bezakan ejaan Jawi LAMA daripada ejaan yang benar-benar salah.
|
|
69
|
+
*
|
|
70
|
+
* Dokumen kurikulum kerap menggunakan ortografi lama. Perbezaannya boleh
|
|
71
|
+
* dipetakan dan tidak bermakna ejaannya salah:
|
|
72
|
+
* ف -> ڤ (fa Arab digunakan untuk bunyi p sebelum ڤ digubal)
|
|
73
|
+
* ج -> چ (jim digunakan untuk bunyi c sebelum چ digubal)
|
|
74
|
+
* ة -> ه (ta marbutah ditukar ha pada sesetengah kata terserap)
|
|
75
|
+
* ك -> ک (kaf Arab lawan kaf Jawi)
|
|
76
|
+
* ي -> ى (ya lawan alif maqsurah)
|
|
77
|
+
*
|
|
78
|
+
* Melaporkannya sebagai "bercanggah" bersama ejaan yang betul-betul salah
|
|
79
|
+
* menenggelamkan yang penting. Ia kelas berasingan.
|
|
80
|
+
*/
|
|
81
|
+
const PASANGAN_LAMA = [
|
|
82
|
+
['ف', 'ڤ'],
|
|
83
|
+
['ج', 'چ'],
|
|
84
|
+
['ة', 'ه'],
|
|
85
|
+
['ك', 'ک'],
|
|
86
|
+
['ي', 'ى'],
|
|
87
|
+
['ݢ', 'ڬ'],
|
|
88
|
+
]
|
|
89
|
+
|
|
90
|
+
function samakanLama(teks) {
|
|
91
|
+
let keluar = teks
|
|
92
|
+
for (const [lama, baharu] of PASANGAN_LAMA) {
|
|
93
|
+
keluar = keluar.split(lama).join(baharu)
|
|
94
|
+
}
|
|
95
|
+
return keluar
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Adakah dua ejaan berbeza HANYA pada pasangan lama/baharu yang diketahui?
|
|
100
|
+
*/
|
|
101
|
+
export function ejaanLamaSahaja(a, b) {
|
|
102
|
+
if (!a || !b || a === b) return false
|
|
103
|
+
return samakanLama(a) === samakanLama(b)
|
|
104
|
+
}
|
package/src/tukar.js
CHANGED
|
@@ -12,6 +12,7 @@ import { lookup } from './prpm.js'
|
|
|
12
12
|
import { normalisasiJawi } from './kamus.js'
|
|
13
13
|
import { cariDataset, muatDataset } from './dataset.js'
|
|
14
14
|
import { cariNama, mungkinAkronim, mungkinNamaKhas } from './nama.js'
|
|
15
|
+
import { semakHurufAkhir } from './semak.js'
|
|
15
16
|
import {
|
|
16
17
|
imbuhAkhiranAn,
|
|
17
18
|
imbuhAkhiranKan,
|
|
@@ -225,10 +226,21 @@ export function petaSongsang(cache, dataset = null) {
|
|
|
225
226
|
* Apabila tiada calon lapisan 1, calon yang belum diketahui digunakan dan
|
|
226
227
|
* sumbernya dilabel, tidak dipulangkan secara senyap.
|
|
227
228
|
*/
|
|
228
|
-
export function pilihCalon(masuk) {
|
|
229
|
+
export function pilihCalon(masuk, cache = null) {
|
|
229
230
|
if (!masuk) return null
|
|
230
231
|
if (Array.isArray(masuk)) return { calon: masuk, sumber: 'campur' }
|
|
231
|
-
|
|
232
|
+
|
|
233
|
+
// Isih ikut kekerapan korpus DBP apabila diketahui: calon yang lebih
|
|
234
|
+
// lazim didahulukan supaya pembaca melihatnya dahulu. Ini MENGISIH,
|
|
235
|
+
// bukan memilih; semua calon tetap dipulangkan.
|
|
236
|
+
const isih = (senarai) => {
|
|
237
|
+
if (!cache || senarai.length < 2) return senarai
|
|
238
|
+
return [...senarai].sort(
|
|
239
|
+
(a, b) => (cache[b]?.rujukanKorpus ?? 0) - (cache[a]?.rujukanKorpus ?? 0),
|
|
240
|
+
)
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (masuk.layak?.length) return { calon: isih(masuk.layak), sumber: 'dbp' }
|
|
232
244
|
if (masuk.entah?.length) return { calon: masuk.entah, sumber: 'dataset' }
|
|
233
245
|
if (masuk.tolak?.length) return { calon: masuk.tolak, sumber: 'rujukan' }
|
|
234
246
|
return null
|
|
@@ -256,6 +268,7 @@ export async function keJawi(teks, cache, opts = {}) {
|
|
|
256
268
|
const kataUlang = []
|
|
257
269
|
const dijanaPedoman = []
|
|
258
270
|
const akronim = []
|
|
271
|
+
const mencurigakan = []
|
|
259
272
|
const bahagian = teks.split(PECAH_RUMI)
|
|
260
273
|
const keluar = []
|
|
261
274
|
|
|
@@ -263,7 +276,15 @@ export async function keJawi(teks, cache, opts = {}) {
|
|
|
263
276
|
if (i % 2 === 0) { keluar.push(cebis); continue }
|
|
264
277
|
|
|
265
278
|
const rec = await lookup(cebis.toLowerCase(), cache, opts)
|
|
266
|
-
if (rec?.jawi) {
|
|
279
|
+
if (rec?.jawi) {
|
|
280
|
+
// PRPM sendiri kadangkala menyimpan ejaan salah pada kata kepala.
|
|
281
|
+
// Ditandakan, bukan ditolak: pengguna perlu melihatnya.
|
|
282
|
+
const curiga = semakHurufAkhir(cebis, rec.jawi)
|
|
283
|
+
if (curiga) mencurigakan.push(curiga)
|
|
284
|
+
keluar.push(rec.jawi)
|
|
285
|
+
dbp.push(cebis)
|
|
286
|
+
continue
|
|
287
|
+
}
|
|
267
288
|
|
|
268
289
|
// Keadaan berbeza daripada tiada entri: DBP mengiktiraf perkataan ini
|
|
269
290
|
// sebagai Melayu, cuma tidak menyimpan ejaan Jawinya. Lazim bagi nama
|
|
@@ -387,6 +408,7 @@ export async function keJawi(teks, cache, opts = {}) {
|
|
|
387
408
|
dijanaPedoman,
|
|
388
409
|
namaKhas: [...new Set(namaKhas)],
|
|
389
410
|
akronim: [...new Set(akronim)],
|
|
411
|
+
mencurigakan,
|
|
390
412
|
adaEntriTanpaJawi: [...new Set(adaEntriTanpaJawi)],
|
|
391
413
|
belumDisahkan: [...new Set(belumDisahkan)],
|
|
392
414
|
terbitanSah,
|
|
@@ -441,7 +463,7 @@ export function keRumi(teksJawi, cache, { penanda = ['«', '»'], dataset = null
|
|
|
441
463
|
}
|
|
442
464
|
if (!bolehGabung) continue
|
|
443
465
|
const frasa = bahagian.slice(i, akhir + 1).join(' ').replace(/\s+/g, ' ')
|
|
444
|
-
const jumpa = pilihCalon(peta.get(normalisasiJawi(frasa)))
|
|
466
|
+
const jumpa = pilihCalon(peta.get(normalisasiJawi(frasa)), cache)
|
|
445
467
|
if (jumpa) { padanan = jumpa.calon; hujung = akhir; break }
|
|
446
468
|
}
|
|
447
469
|
|