prpm-dbp 0.19.0 → 0.20.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/SKILL.md +29 -0
- package/bin/cli.js +13 -0
- package/package.json +1 -1
- package/src/gate.js +41 -14
- package/src/semak.js +49 -0
- package/src/tukar.js +13 -4
package/SKILL.md
CHANGED
|
@@ -43,6 +43,35 @@ Empat perkara yang ditangkap dalam teks Jawi:
|
|
|
43
43
|
|
|
44
44
|
Ruang tersasar itu kes paling berbahaya. Separuh perkataan boleh **berjaya secara palsu**: `ݢول` benar-benar memetakan kepada dua perkataan DBP tulen, `gol` dan `gul`. Penukar yang memilih calon paling kerap akan mengeluarkan "gol" dengan yakin, dan separuh perkataan hilang tanpa sesiapa perasan. Sebab itu percantuman dicuba apabila **salah satu** jiran belum selesai, bukan kedua-duanya.
|
|
45
45
|
|
|
46
|
+
## Teks Arab dan al-Quran TIDAK PERNAH ditukar
|
|
47
|
+
|
|
48
|
+
Pedoman §11.1: petikan al-Quran, hadis, nama surah, nama tokoh dan nama tempat **kekal ejaan Arab asalnya**. Ia tidak ditransliterasi ke Rumi dan tidak pernah dicadangkan pembetulan ejaan Melayu.
|
|
49
|
+
|
|
50
|
+
Ini bukan teori. `الدين` (ad-Din) memetakan kepada dua entri dalam data, jadi penukar lama mengeluarkan `addin|al-din` untuk teks Arab tulen dan pembaca tidak nampak apa-apa salah.
|
|
51
|
+
|
|
52
|
+
Dua isyarat automatik, kedua-duanya diukur pada 66,323 ejaan Jawi DBP sebenar:
|
|
53
|
+
|
|
54
|
+
| Isyarat | Padanan palsu | Contoh dilindungi |
|
|
55
|
+
|---|---|---|
|
|
56
|
+
| tanda baris (harakat) | **0** | `إِنَّ ٱللَّهَ مَعَ ٱلصَّٰبِرِينَ` |
|
|
57
|
+
| `أ` atau `آ` di awal | 5 (0.008%) | `أصحاب` |
|
|
58
|
+
| `ال` di awal, tidak dikenali | — | `اليمين`, `المقربون`, `الفاتحة` |
|
|
59
|
+
|
|
60
|
+
Petikan al-Quran datang bertanda baris dalam amalan biasa, jadi ia dilindungi tanpa apa-apa persediaan.
|
|
61
|
+
|
|
62
|
+
**Istilah Arab telanjang** — yang tiada tanda baris dan padan sesuatu dalam kamus, seperti `الدين` — diisytihar sekali:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
npx prpm-dbp nama ad-din الدين --jenis arab
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Selepas itu ia tidak disentuh lagi, dalam `rumi` mahupun `gate`.
|
|
69
|
+
|
|
70
|
+
**Dua perkara yang sengaja TIDAK dibuat**, kedua-duanya diuji dan ditolak:
|
|
71
|
+
|
|
72
|
+
1. **Rentang Arab tidak merebak kepada jiran.** Kelihatan betul sehingga diuji: satu ejaan Melayu yang salah di sebelah petikan al-Quran tidak dapat dibezakan daripada satu perkataan Arab di sebelah petikan al-Quran, jadi `منجاوهكن` diserap dan kesilapan sebenar hilang. Mendiamkan ralat tulen lebih buruk daripada menandakan satu perkataan Arab yang jarang.
|
|
73
|
+
2. **Jaminan DBP tidak dituntut untuk mengatasi isyarat `ال`.** Ia membekukan 325 daripada 346 perkataan ber-`ال`, termasuk `alah`, `alaf` dan `alabaster`, yang alif-lamnya sebahagian perkataan dan bukan kata sandang. Ortografi sahaja tidak dapat membezakannya.
|
|
74
|
+
|
|
46
75
|
Nama khas Arab tanpa kata sandang `ال` (`أصحاب`, nama tokoh, nama tempat) tidak dapat disahkan kamus Melayu. Isytiharkannya melalui `npx prpm-dbp nama <rumi> <jawi> --jenis arab`; selepas itu ia berhenti menjadi ralat, tetapi tetap **bukan** lulus DBP.
|
|
47
76
|
|
|
48
77
|
`gate` keluar dengan kod 1 apabila ada ralat, jadi ia boleh dipasang sebagai hook atau langkah CI. Peraturan yang bergantung pada agen ingat sendiri akan gagal, dan gagal secara senyap.
|
package/bin/cli.js
CHANGED
|
@@ -7,6 +7,17 @@ import { banding, cariKamus } from '../src/kamus.js'
|
|
|
7
7
|
import { keJawi, keRumi, semakJawi } from '../src/tukar.js'
|
|
8
8
|
import { huraiTsv, muatDataset, simpanDataset } from '../src/dataset.js'
|
|
9
9
|
import { NAMA_PATH, muatNama, tambahNama } from '../src/nama.js'
|
|
10
|
+
|
|
11
|
+
// Istilah Arab yang pengguna sahkan sendiri. Petikan al-Quran datang
|
|
12
|
+
// bertanda baris dalam amalan biasa dan dikenali automatik; istilah
|
|
13
|
+
// telanjang seperti الدين diisytihar sekali di sini dan tidak pernah
|
|
14
|
+
// ditransliterasi lagi, dalam `rumi` mahupun `gate`.
|
|
15
|
+
const arabDiisytiharkan = async () =>
|
|
16
|
+
new Set(
|
|
17
|
+
Object.values(await muatNama())
|
|
18
|
+
.filter((n) => n?.jenis === 'arab' && n.jawi)
|
|
19
|
+
.map((n) => n.jawi),
|
|
20
|
+
)
|
|
10
21
|
import { gate, kodKeluar } from '../src/gate.js'
|
|
11
22
|
import {
|
|
12
23
|
CACHE_PATH,
|
|
@@ -252,6 +263,7 @@ async function main() {
|
|
|
252
263
|
pilihLazim: pilihan.pilih,
|
|
253
264
|
padanRangka: pilihan.rangka,
|
|
254
265
|
glosari: pilihan.glosari ? JSON.parse(await readFile(pilihan.glosari, 'utf8')) : null,
|
|
266
|
+
arabDiisytihar: await arabDiisytiharkan(),
|
|
255
267
|
})
|
|
256
268
|
if (pilihan.json) { keluar(h); break }
|
|
257
269
|
console.log(h.teks)
|
|
@@ -330,6 +342,7 @@ async function main() {
|
|
|
330
342
|
.map((n) => n?.jawi)
|
|
331
343
|
.filter(Boolean),
|
|
332
344
|
),
|
|
345
|
+
arabDiisytihar: await arabDiisytiharkan(),
|
|
333
346
|
luarTalian: pilihan.luarTalian !== false,
|
|
334
347
|
abai,
|
|
335
348
|
})
|
package/package.json
CHANGED
package/src/gate.js
CHANGED
|
@@ -29,7 +29,7 @@ import { cariDataset } from './dataset.js'
|
|
|
29
29
|
import { normalisasiJawi } from './kamus.js'
|
|
30
30
|
import { jarakHuruf, petaSongsang } from './tukar.js'
|
|
31
31
|
import { pisahUlangJawi } from './pedoman.js'
|
|
32
|
-
import { rangkaKonsonan } from './semak.js'
|
|
32
|
+
import { isyaratArab, rangkaKonsonan } from './semak.js'
|
|
33
33
|
|
|
34
34
|
const TOKEN_RUMI = /([A-Za-zÀ-ÿ][A-Za-zÀ-ÿ'-]*)/g
|
|
35
35
|
const TOKEN_JAWI = /([-ۿݐ-ݿﭐ-﷿ﹰ-]+)/g
|
|
@@ -55,7 +55,7 @@ const HARAKAT = /[ً-ْٰٓ-ٕ]/
|
|
|
55
55
|
* kerana separuh pertama "berjaya".
|
|
56
56
|
*/
|
|
57
57
|
export function gateJawi(teks, cache, opts = {}) {
|
|
58
|
-
const { dataset = null, abai = new Set(), namaJawi = new Set() } = opts
|
|
58
|
+
const { dataset = null, abai = new Set(), namaJawi = new Set(), arabDiisytihar = new Set() } = opts
|
|
59
59
|
const peta = petaSongsang(cache, dataset)
|
|
60
60
|
|
|
61
61
|
// Indeks rangka konsonan, dibina sekali. Ejaan yang tersalah biasanya
|
|
@@ -113,21 +113,48 @@ export function gateJawi(teks, cache, opts = {}) {
|
|
|
113
113
|
return m ? m.layak.length === 1 : false
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
// ── Kesan rentang Arab SEBELUM apa-apa dinilai ────────────────────────
|
|
117
|
+
//
|
|
118
|
+
// Pedoman §11.1: petikan al-Quran, hadis, nama surah, nama tokoh dan nama
|
|
119
|
+
// tempat KEKAL ejaan Arab asalnya. Ia tidak boleh disemak dengan kamus
|
|
120
|
+
// Melayu, dan lebih penting lagi ia tidak boleh DICADANGKAN pembetulan.
|
|
121
|
+
// Gate hanya melapor, tetapi agen yang membaca "guna اصحاب" akan menyunting
|
|
122
|
+
// ayat al-Quran. Mencadangkan pembetulan pada teks wahyu lebih teruk
|
|
123
|
+
// daripada tidak menyemaknya langsung.
|
|
124
|
+
//
|
|
125
|
+
// Diukur pada 66,323 ejaan Jawi DBP sebenar:
|
|
126
|
+
// harakat 0 padanan -> isyarat sempurna
|
|
127
|
+
// `أ` di awal 4 padanan -> 0.006%, dan keempat-empatnya larut sendiri
|
|
128
|
+
// `إ` di awal 7 padanan -> termasuk إسلام, jadi BUKAN isyarat
|
|
129
|
+
// `ال` di awal 360 padanan -> hanya berguna bersama syarat tidak larut
|
|
130
|
+
const benihArab = (t) => isyaratArab(t, (x) => Boolean(cari(x)), arabDiisytihar)
|
|
131
|
+
|
|
132
|
+
const arab = new Set()
|
|
133
|
+
for (const tok of token) if (benihArab(tok.teks)) arab.add(tok.i)
|
|
134
|
+
|
|
135
|
+
// Rentang Arab SENGAJA TIDAK merebak kepada jiran. Percubaan merebak
|
|
136
|
+
// melalui ruang kepada token yang tidak larut kelihatan betul sehingga
|
|
137
|
+
// diuji: satu ejaan Melayu yang salah di sebelah petikan al-Quran tidak
|
|
138
|
+
// dapat dibezakan daripada satu perkataan Arab di sebelah petikan
|
|
139
|
+
// al-Quran, jadi منجاوهكن diserap dan kesilapan sebenar hilang.
|
|
140
|
+
//
|
|
141
|
+
// Mendiamkan ralat tulen lebih buruk daripada menandakan satu perkataan
|
|
142
|
+
// Arab yang jarang. Setiap token mesti membawa isyaratnya sendiri.
|
|
143
|
+
// Petikan al-Quran datang bertanda baris dalam amalan biasa, jadi ia
|
|
144
|
+
// dilindungi; apa yang tidak, diisytihar melalui `nama` atau `--abai`.
|
|
145
|
+
|
|
116
146
|
for (const [n, tok] of token.entries()) {
|
|
117
147
|
const t = tok.teks
|
|
118
148
|
if (abai.has(t) || abai.has(normalisasiJawi(t))) continue
|
|
119
149
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
// Pedoman §11.1: petikan Arab kekalkan ejaan asal. Ia bukan lulus DBP
|
|
129
|
-
// kerana kamus Melayu tidak boleh mengesahkannya; ia keadaan ketiga.
|
|
130
|
-
if (!dilihat.has(t)) { dilihat.add(t); tidakDapatDisahkan.push({ kata: t, sebab: 'teks Arab, dikekalkan (Pedoman §11.1)' }) }
|
|
150
|
+
if (arab.has(tok.i)) {
|
|
151
|
+
// Bukan lulus DBP: kamus Melayu tidak mengesahkannya. Bukan ralat:
|
|
152
|
+
// ejaan Arab asal memang betul. Ia keadaan ketiga, dan TIADA cadangan
|
|
153
|
+
// pembetulan dikeluarkan untuknya.
|
|
154
|
+
if (!dilihat.has(t)) {
|
|
155
|
+
dilihat.add(t)
|
|
156
|
+
tidakDapatDisahkan.push({ kata: t, sebab: 'teks Arab, kekalkan seadanya (Pedoman §11.1)' })
|
|
157
|
+
}
|
|
131
158
|
continue
|
|
132
159
|
}
|
|
133
160
|
|
|
@@ -136,7 +163,7 @@ export function gateJawi(teks, cache, opts = {}) {
|
|
|
136
163
|
// Cuba cantum dengan jiran SEBELUM melaporkan apa-apa, kerana ruang
|
|
137
164
|
// tersasar mencemarkan kedua-dua separuh sekali gus.
|
|
138
165
|
const seterusnya = token[n + 1]
|
|
139
|
-
if (seterusnya && ruangSahaja(tok.i, seterusnya.i)) {
|
|
166
|
+
if (seterusnya && !arab.has(seterusnya.i) && ruangSahaja(tok.i, seterusnya.i)) {
|
|
140
167
|
const belumSelesai = !selesai(t) || !selesai(seterusnya.teks)
|
|
141
168
|
if (belumSelesai) {
|
|
142
169
|
const gabung = cari(t + seterusnya.teks)
|
package/src/semak.js
CHANGED
|
@@ -152,3 +152,52 @@ export function rangkaKonsonan(jawi) {
|
|
|
152
152
|
if (keluar && HUJUNG_SETARA.has(keluar.slice(-1))) keluar = keluar.slice(0, -1) + 'ه'
|
|
153
153
|
return keluar
|
|
154
154
|
}
|
|
155
|
+
|
|
156
|
+
// ── Pengesan teks Arab ────────────────────────────────────────────────────
|
|
157
|
+
//
|
|
158
|
+
// Pedoman DBP §11.1: petikan al-Quran, hadis, nama surah, nama tokoh dan
|
|
159
|
+
// nama tempat KEKAL ejaan Arab asalnya. Ia tidak boleh ditransliterasi ke
|
|
160
|
+
// Rumi dan tidak boleh dicadangkan pembetulan ejaan Melayu.
|
|
161
|
+
//
|
|
162
|
+
// Ini bukan sekadar teori. `الدين` (ad-Din) memetakan kepada dua entri Jawi
|
|
163
|
+
// dalam data DBP, jadi tanpa pemeriksaan ini penukar mengeluarkan
|
|
164
|
+
// "addin|al-din" untuk teks Arab dan pembaca tidak nampak apa-apa salah.
|
|
165
|
+
//
|
|
166
|
+
// Diukur pada 66,323 ejaan Jawi DBP sebenar:
|
|
167
|
+
// harakat 0 padanan -> isyarat sempurna
|
|
168
|
+
// `أ` `آ` awal 5 padanan -> 0.008%
|
|
169
|
+
// `ال` awal 360 padanan -> berguna HANYA bersama syarat tidak larut
|
|
170
|
+
// `إ` awal 7 padanan -> termasuk إسلام, jadi BUKAN isyarat
|
|
171
|
+
const HARAKAT = /[ً-ْٰٓ-ٕ]/
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* @param {string} token satu perkataan Jawi/Arab
|
|
175
|
+
* @param {(t: string) => boolean} larut adakah token ini dikenali sebagai
|
|
176
|
+
* kata Melayu langsung
|
|
177
|
+
* @param {Set<string>} diisytihar istilah Arab yang pengguna sahkan sendiri
|
|
178
|
+
*
|
|
179
|
+
* Menuntut jaminan DBP lapisan 1 untuk mengatasi isyarat `ال` telah diuji
|
|
180
|
+
* dan DITOLAK: ia membekukan 325 daripada 346 perkataan ber-`ال`, termasuk
|
|
181
|
+
* alah, alaf dan alabaster, yang alif-lamnya sebahagian perkataan dan bukan
|
|
182
|
+
* kata sandang. Ortografi sahaja tidak dapat membezakannya.
|
|
183
|
+
*
|
|
184
|
+
* Yang tinggal ialah dua isyarat yang benar-benar bersih, ditambah
|
|
185
|
+
* pengisytiharan pengguna. Petikan al-Quran datang bertanda baris dalam
|
|
186
|
+
* amalan biasa; istilah Arab telanjang seperti `الدين` diisytihar sekali
|
|
187
|
+
* melalui simpanan `nama --jenis arab` dan tidak pernah disentuh lagi.
|
|
188
|
+
*
|
|
189
|
+
* Isyarat sengaja per-token dan TIDAK merebak kepada jiran. Percubaan
|
|
190
|
+
* merebak melalui ruang kelihatan betul sehingga diuji: satu ejaan Melayu
|
|
191
|
+
* yang salah di sebelah petikan al-Quran tidak dapat dibezakan daripada satu
|
|
192
|
+
* perkataan Arab di sebelah petikan al-Quran, jadi ralat tulen diserap dan
|
|
193
|
+
* hilang. Mendiamkan kesilapan sebenar lebih buruk daripada menandakan satu
|
|
194
|
+
* perkataan Arab yang jarang.
|
|
195
|
+
*/
|
|
196
|
+
export function isyaratArab(token, larut = () => false, diisytihar = new Set()) {
|
|
197
|
+
if (HARAKAT.test(token)) return 'harakat'
|
|
198
|
+
if (diisytihar.has(token)) return 'diisytihar pengguna'
|
|
199
|
+
if (larut(token)) return null
|
|
200
|
+
if (/^[أآ]/.test(token)) return 'hamzah awal'
|
|
201
|
+
if (/^ال/.test(token)) return 'kata sandang'
|
|
202
|
+
return null
|
|
203
|
+
}
|
package/src/tukar.js
CHANGED
|
@@ -12,7 +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 { rangkaKonsonan, semakHurufAkhir } from './semak.js'
|
|
15
|
+
import { isyaratArab, rangkaKonsonan, semakHurufAkhir } from './semak.js'
|
|
16
16
|
import {
|
|
17
17
|
imbuhAkhiranAn,
|
|
18
18
|
imbuhAkhiranKan,
|
|
@@ -491,6 +491,7 @@ export function keRumi(
|
|
|
491
491
|
pilihLazim = false,
|
|
492
492
|
padanRangka = false,
|
|
493
493
|
glosari = null,
|
|
494
|
+
arabDiisytihar = new Set(),
|
|
494
495
|
} = {},
|
|
495
496
|
) {
|
|
496
497
|
const peta = petaSongsang(cache, dataset)
|
|
@@ -521,9 +522,17 @@ export function keRumi(
|
|
|
521
522
|
}
|
|
522
523
|
}
|
|
523
524
|
|
|
524
|
-
// Teks Arab
|
|
525
|
-
//
|
|
526
|
-
|
|
525
|
+
// Teks Arab dibiarkan seadanya. Cuba menukarnya akan merosakkan petikan
|
|
526
|
+
// al-Quran dan hadis dalam dokumen Melayu.
|
|
527
|
+
//
|
|
528
|
+
// Tanda baris sahaja TIDAK memadai. Frasa seperti أصحاب اليمين datang
|
|
529
|
+
// tanpa baris, dan lebih buruk lagi `الدين` memetakan kepada dua entri
|
|
530
|
+
// Jawi dalam data DBP — jadi penukar mengeluarkan "addin|al-din" untuk
|
|
531
|
+
// teks Arab tulen dan pembaca tidak nampak apa-apa salah. Perkataan
|
|
532
|
+
// yang benar-benar larut dalam DBP tidak terjejas: isyarat kata sandang
|
|
533
|
+
// hanya terpakai apabila perkataan itu bukan kata Melayu.
|
|
534
|
+
const tandaArab = isyaratArab(bahagian[i], (t) => peta.has(normalisasiJawi(t)), arabDiisytihar)
|
|
535
|
+
if (tandaArab) {
|
|
527
536
|
arab.push(bahagian[i])
|
|
528
537
|
keluar.push(bahagian[i])
|
|
529
538
|
continue
|