prpm-dbp 0.7.0 → 0.9.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 +24 -2
- package/bin/cli.js +7 -3
- package/package.json +1 -1
- package/src/nama.js +17 -0
- package/src/tukar.js +47 -2
package/SKILL.md
CHANGED
|
@@ -28,7 +28,7 @@ npx prpm-dbp lookup murid sekolah
|
|
|
28
28
|
# semak setiap perkataan Rumi dalam fail wujud dalam PRPM
|
|
29
29
|
npx prpm-dbp audit nota.txt
|
|
30
30
|
|
|
31
|
-
# semak ejaan Jawi lawan teks Rumi sejajar
|
|
31
|
+
# semak ejaan Jawi lawan teks Rumi sejajar
|
|
32
32
|
npx prpm-dbp audit-jawi rph-jawi.txt rph-rumi.txt
|
|
33
33
|
|
|
34
34
|
# panaskan cache dari senarai perkataan, bayar sekali guna selamanya
|
|
@@ -187,6 +187,28 @@ Peraturan yang dilaksanakan: §13.5-13.7 (awalan `di-`, `se-`, `ke-`), §13.10 (
|
|
|
187
187
|
|
|
188
188
|
**Kodpoint setiap imbuhan disahkan empirik daripada dataset, bukan dibaca daripada peraturan.** Hamzah dalam akhiran ialah `ء` U+0621 berdiri sendiri, manakala dalam awalan ia `أ` U+0623 di atas alif. Peraturan bertulis tidak membezakan dua itu.
|
|
189
189
|
|
|
190
|
+
## Apa yang sebenarnya tercicir
|
|
191
|
+
|
|
192
|
+
Diuji terhadap satu pelajaran Pendidikan Islam Tingkatan 4 sebenar (406 perkataan):
|
|
193
|
+
|
|
194
|
+
```
|
|
195
|
+
disahkan DBP 223 | dari dataset 38 | kata sendi §14.1 3
|
|
196
|
+
nama khas 15 | belum disahkan 10
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
**Tiada satu pun kata Melayu biasa tercicir.** Yang tinggal terbahagi kepada dua kategori sahaja:
|
|
200
|
+
|
|
201
|
+
| Kategori | Contoh | Selesaian |
|
|
202
|
+
|---|---|---|
|
|
203
|
+
| Istilah tajwid Arab | `Qasirah`, `Tawilah`, `Mukhaffaf`, `Muthaqqal`, `Sughra`, `Kubra`, `Waqaf`, `hijaiyah` | `nama` dengan `jenis: istilah`, sahkan sekali |
|
|
204
|
+
| Akronim | `KPM`, `DBP`, `KSSM`, `PBD`, `KBAT` | dilaporkan berasingan, kekal Rumi |
|
|
205
|
+
|
|
206
|
+
Inilah sebab transliterator pola suku kata (§7 hingga §9) **tidak dilaksanakan**. Ia tidak akan menyentuh satu pun perkataan di atas. Lebih buruk lagi, istilah tajwid itu kata serapan Arab, dan §11.1 menetapkan ejaan Jawinya ialah ejaan Arab asal — peraturan pola Melayu akan menghasilkan ejaan fonetik yang salah untuk setiap satu, iaitu tepat di tempat ia paling merosakkan.
|
|
207
|
+
|
|
208
|
+
**`audit-jawi` menggunakan enjin penukaran yang sama dengan `jawi`.** Teks Rumi ditukar dahulu, kemudian dibandingkan. Ini penting kerana §14.1 dan §17.1 mengubah bilangan token: `Murid ke sekolah` ialah tiga perkataan Rumi tetapi dua perkataan Jawi. Perbandingan kedudukan yang naif akan melaporkan output alat ini sendiri sebagai salah.
|
|
209
|
+
|
|
210
|
+
Dengan cara ini hanya ada **satu takrif "Jawi yang betul"** dalam pakej, bukan dua yang boleh menyimpang.
|
|
211
|
+
|
|
190
212
|
## Rantai kepercayaan
|
|
191
213
|
|
|
192
214
|
Setiap ejaan Jawi yang dikeluarkan membawa asal-usulnya. Jangan runtuhkan lapisan ini.
|
|
@@ -226,7 +248,7 @@ Bila menulis teks BM atau Jawi yang akan dihantar kepada orang:
|
|
|
226
248
|
|---|---|---|
|
|
227
249
|
| `lulus` / `padan` | ada dalam PRPM, ejaan sepadan | tiada |
|
|
228
250
|
| `belumDisahkan` | tiada entri dalam PRPM | semak ejaan, atau isytihar sebagai nama khas |
|
|
229
|
-
| `salahEjaan` | Jawi ditulis tidak sama dengan
|
|
251
|
+
| `salahEjaan` | Jawi ditulis tidak sama dengan hasil penukaran | ganti dengan medan `sepatutnya` |
|
|
230
252
|
| `tiadaRujukan` | PRPM tiada ejaan Jawi untuk kata itu | kekalkan, tanda kepada pengguna |
|
|
231
253
|
| `gagalSemak` | rangkaian gagal | cuba semula, jangan anggap salah |
|
|
232
254
|
|
package/bin/cli.js
CHANGED
|
@@ -4,7 +4,7 @@ import process from 'node:process'
|
|
|
4
4
|
|
|
5
5
|
import { pasangSkill } from '../src/install.js'
|
|
6
6
|
import { banding, cariKamus } from '../src/kamus.js'
|
|
7
|
-
import { keJawi, keRumi } from '../src/tukar.js'
|
|
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
10
|
import {
|
|
@@ -49,6 +49,7 @@ function huraiArgs(argv) {
|
|
|
49
49
|
else if (a === '--link') pilihan.link = true
|
|
50
50
|
else if (a === '--cepat') pilihan.cepat = true
|
|
51
51
|
else if (a === '--pedoman') pilihan.pedoman = true
|
|
52
|
+
else if (a === '--jenis') pilihan.jenis = argv[++i]
|
|
52
53
|
else if (a === '--had') pilihan.had = Number(argv[++i])
|
|
53
54
|
else if (a === '--delay') pilihan.delay = Number(argv[++i])
|
|
54
55
|
else if (a === '--buang') pilihan.buang = argv[++i]
|
|
@@ -102,11 +103,11 @@ async function main() {
|
|
|
102
103
|
case 'audit-jawi': {
|
|
103
104
|
const [failJawi, failRumi] = baki
|
|
104
105
|
if (!failJawi || !failRumi) throw new Error('perlu dua fail: <jawi> <rumi>')
|
|
105
|
-
const laporan = await
|
|
106
|
+
const laporan = await semakJawi(
|
|
106
107
|
await readFile(failJawi, 'utf8'),
|
|
107
108
|
await readFile(failRumi, 'utf8'),
|
|
108
109
|
cache,
|
|
109
|
-
opts,
|
|
110
|
+
{ ...opts, cubaDasar: !pilihan.cepat, janaImbuhan: pilihan.pedoman },
|
|
110
111
|
)
|
|
111
112
|
await saveCache(cache)
|
|
112
113
|
keluar(laporan)
|
|
@@ -175,6 +176,9 @@ async function main() {
|
|
|
175
176
|
h.dijanaPedoman.map((x) => `${x.kata}=${x.jawi} [${x.rujukan}]`).join(', '),
|
|
176
177
|
)
|
|
177
178
|
}
|
|
179
|
+
if (h.akronim.length) {
|
|
180
|
+
console.error(`akronim, dikekalkan Rumi (${h.akronim.length}): ${h.akronim.join(', ')}`)
|
|
181
|
+
}
|
|
178
182
|
if (h.namaKhas.length) {
|
|
179
183
|
console.error(
|
|
180
184
|
`nama khas belum disahkan (${h.namaKhas.length}): ${h.namaKhas.join(', ')}\n` +
|
package/package.json
CHANGED
package/src/nama.js
CHANGED
|
@@ -55,6 +55,23 @@ export async function cariNama(kata) {
|
|
|
55
55
|
return { ...rec, dijumpai: true, disahkanDbp: false }
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
/**
|
|
59
|
+
* §19 — Akronim dan singkatan.
|
|
60
|
+
*
|
|
61
|
+
* Singkatan inisialisme (KPM, DBP, PDF, KSSM, PBD, KBAT) ialah kategori
|
|
62
|
+
* tersendiri, bukan salah eja dan bukan nama orang. Pedoman §19.3 dan §19.4
|
|
63
|
+
* menetapkan ia ditulis dengan huruf abjad Jawi berserta titik selepas
|
|
64
|
+
* setiap huruf, tetapi banyak dokumen pendidikan mengekalkannya dalam Rumi
|
|
65
|
+
* dengan sengaja. Alat ini tidak memutuskan bagi pihak anda: ia hanya
|
|
66
|
+
* mengasingkan akronim supaya keputusan itu dibuat sekali, bukan dianggap
|
|
67
|
+
* sebagai ejaan yang salah.
|
|
68
|
+
*/
|
|
69
|
+
export function mungkinAkronim(kata) {
|
|
70
|
+
const k = String(kata)
|
|
71
|
+
// Dua huruf besar atau lebih berturut-turut, tanpa huruf kecil.
|
|
72
|
+
return /^[A-Z][A-Z0-9]+$/.test(k) && k.length >= 2
|
|
73
|
+
}
|
|
74
|
+
|
|
58
75
|
/**
|
|
59
76
|
* Teka sama ada perkataan itu nama khas.
|
|
60
77
|
*
|
package/src/tukar.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
import { lookup } from './prpm.js'
|
|
12
12
|
import { normalisasiJawi } from './kamus.js'
|
|
13
13
|
import { cariDataset, muatDataset } from './dataset.js'
|
|
14
|
-
import { cariNama, mungkinNamaKhas } from './nama.js'
|
|
14
|
+
import { cariNama, mungkinAkronim, mungkinNamaKhas } from './nama.js'
|
|
15
15
|
import {
|
|
16
16
|
imbuhAkhiranAn,
|
|
17
17
|
imbuhAkhiranI,
|
|
@@ -194,6 +194,7 @@ export async function keJawi(teks, cache, opts = {}) {
|
|
|
194
194
|
const namaKhas = []
|
|
195
195
|
const kataUlang = []
|
|
196
196
|
const dijanaPedoman = []
|
|
197
|
+
const akronim = []
|
|
197
198
|
const bahagian = teks.split(PECAH_RUMI)
|
|
198
199
|
const keluar = []
|
|
199
200
|
|
|
@@ -268,7 +269,11 @@ export async function keJawi(teks, cache, opts = {}) {
|
|
|
268
269
|
// Nama khas yang belum disahkan bukan salah eja. Ia dilaporkan
|
|
269
270
|
// berasingan supaya pengguna tahu apa yang perlu disahkan sekali, dan
|
|
270
271
|
// bukan mengulang menyemak ejaan yang memang betul.
|
|
271
|
-
|
|
272
|
+
// §19: akronim ialah kategori tersendiri. Ia bukan salah eja dan bukan
|
|
273
|
+
// nama orang, jadi mencampurkannya dengan dua-dua itu menyembunyikan
|
|
274
|
+
// apa yang benar-benar perlu disemak.
|
|
275
|
+
if (mungkinAkronim(cebis)) akronim.push(cebis)
|
|
276
|
+
else if (mungkinNamaKhas(cebis, bahagian[i - 1])) namaKhas.push(cebis)
|
|
272
277
|
else belumDisahkan.push(cebis)
|
|
273
278
|
keluar.push(penanda[0] + cebis + penanda[1])
|
|
274
279
|
}
|
|
@@ -300,6 +305,7 @@ export async function keJawi(teks, cache, opts = {}) {
|
|
|
300
305
|
kataUlang,
|
|
301
306
|
dijanaPedoman,
|
|
302
307
|
namaKhas: [...new Set(namaKhas)],
|
|
308
|
+
akronim: [...new Set(akronim)],
|
|
303
309
|
adaEntriTanpaJawi: [...new Set(adaEntriTanpaJawi)],
|
|
304
310
|
belumDisahkan: [...new Set(belumDisahkan)],
|
|
305
311
|
terbitanSah,
|
|
@@ -401,3 +407,42 @@ export function keRumi(teksJawi, cache, { penanda = ['«', '»'], dataset = null
|
|
|
401
407
|
lengkap: tidakDikenali.length === 0,
|
|
402
408
|
}
|
|
403
409
|
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Semak teks Jawi terhadap teks Rumi sejajar, menggunakan enjin penukaran
|
|
413
|
+
* yang SAMA dengan `keJawi`.
|
|
414
|
+
*
|
|
415
|
+
* Versi lama membandingkan token ke-N lawan token ke-N. Itu pecah sebaik
|
|
416
|
+
* sahaja Pedoman §14.1 (kata sendi bercantum) dan §17.1 (kata ulang) mula
|
|
417
|
+
* digunakan, kerana kedua-duanya mengubah bilangan token: `Murid ke sekolah`
|
|
418
|
+
* ialah tiga perkataan Rumi tetapi dua perkataan Jawi. Akibatnya alat ini
|
|
419
|
+
* melaporkan outputnya sendiri sebagai salah.
|
|
420
|
+
*
|
|
421
|
+
* Sekarang teks Rumi ditukar dahulu, kemudian dibandingkan token demi token
|
|
422
|
+
* terhadap hasil itu. Dengan cara ini hanya ada satu takrif "Jawi yang betul"
|
|
423
|
+
* dalam pakej.
|
|
424
|
+
*/
|
|
425
|
+
export async function semakJawi(jawiTeks, rumiTeks, cache, opts = {}) {
|
|
426
|
+
const jangkaan = await keJawi(rumiTeks, cache, opts)
|
|
427
|
+
const tokJangka = [...jangkaan.teks.matchAll(new RegExp(`[${JAWI_JULAT}\u0660-\u0669]+`, 'g'))].map((m) => m[0])
|
|
428
|
+
const tokAda = [...jawiTeks.matchAll(new RegExp(`[${JAWI_JULAT}\u0660-\u0669]+`, 'g'))].map((m) => m[0])
|
|
429
|
+
|
|
430
|
+
const salahEjaan = []
|
|
431
|
+
let padan = 0
|
|
432
|
+
for (const [i, jangka] of tokJangka.entries()) {
|
|
433
|
+
const ditulis = tokAda[i] ?? null
|
|
434
|
+
if (ditulis && normalisasiJawi(ditulis) === normalisasiJawi(jangka)) padan++
|
|
435
|
+
else salahEjaan.push({ kedudukan: i, ditulis, sepatutnya: jangka })
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
return {
|
|
439
|
+
tokenJawi: tokAda.length,
|
|
440
|
+
tokenDijangka: tokJangka.length,
|
|
441
|
+
kiraanTakSama: tokAda.length !== tokJangka.length,
|
|
442
|
+
padan,
|
|
443
|
+
salahEjaan,
|
|
444
|
+
belumDisahkan: jangkaan.belumDisahkan,
|
|
445
|
+
namaKhas: jangkaan.namaKhas,
|
|
446
|
+
akronim: jangkaan.akronim,
|
|
447
|
+
}
|
|
448
|
+
}
|