prpm-dbp 0.11.1 → 0.13.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 CHANGED
@@ -176,6 +176,7 @@ Ia mati secara lalai kerana ketepatannya diukur, bukan diandaikan. Diuji terhada
176
176
  | `di-` | 2,435 | 92% |
177
177
  | `se-` | 1,605 | 90% |
178
178
  | `ke-` | 976 | 88% |
179
+ | `-kan` | 3,046 | 92% |
179
180
 
180
181
  Sebahagian kegagalan itu artifak ukuran (`tamu` bukan `ta`+`mu`), jadi ketepatan sebenar lebih tinggi. Walaupun begitu, 98% tidak cukup untuk menjana ejaan secara senyap dalam alat yang seluruh tujuannya menghalang Jawi yang nampak betul tetapi salah.
181
182
 
@@ -187,7 +188,9 @@ $ npx prpm-dbp jawi nota.txt --pedoman
187
188
  dijana ikut Pedoman, BELUM disahkan DBP (1): Abadinya=اباديڽ [Pedoman DBP §15.1]
188
189
  ```
189
190
 
190
- Peraturan yang dilaksanakan: §13.5-13.7 (awalan `di-`, `se-`, `ke-`), §13.10 (tiga cara akhiran `-an`), §13.11 (tiga cara akhiran `-i`), §15.1 (kata ganti singkat `ku`, `mu`, `nya`).
191
+ Peraturan yang dilaksanakan: §13.5-13.7 (awalan `di-`, `se-`, `ke-`), §13.9 (akhiran `-kan`), §13.10 (tiga cara akhiran `-an`), §13.11 (tiga cara akhiran `-i`), §15.1 (kata ganti singkat `ku`, `mu`, `nya`).
192
+
193
+ **Awas terhadap nota panduan tidak rasmi.** Nota sekolah sering menyatakan "huruf alif ditambah semula apabila menerima imbuhan" sebagai peraturan menyeluruh. Diuji terhadap data sebenar, itu tidak benar: `suka` سوک menjadi `sukakan` سوککن dan `lukanya` لوکڽ, kedua-duanya tanpa alif. Penambahan alif berlaku bagi `-an` dan `-i` sahaja, menurut Catatan §13.10(ii) dan §13.11(i).
191
194
 
192
195
  **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.
193
196
 
@@ -272,6 +275,29 @@ Siri yang sama membezakan kaf Arab daripada kaf Jawi secara eksplisit, iaitu per
272
275
 
273
276
  Buku itu berhak cipta terpelihara (PNM). Ia dirujuk di sini sebagai pengesahan, bukan disalin.
274
277
 
278
+ ## Homograf
279
+
280
+ Ejaan Jawi menggunakan empat huruf vokal berbanding enam dalam Rumi, jadi perkataan berbeza kerap berkongsi ejaan. Diukur pada 64,251 ejaan Jawi dalam data: **2,009 (3%) ialah homograf**.
281
+
282
+ Panduan DBP membahagikannya kepada dua:
283
+
284
+ | Jenis | Layanan | Contoh |
285
+ |---|---|---|
286
+ | **Hakiki** | **dibenarkan kekal**, tidak dibezakan | `biru`/`biro` بيرو, `burung`/`borong` بوروڠ, `satu`/`sato` ساتو |
287
+ | **Tidak hakiki** | dibezakan dengan alif | `kampung` کامڤوڠ / `kempung` کمڤوڠ |
288
+
289
+ Jenis kedua ialah Pedoman §8.17 (juga Klinik Jawi Siri ke-35): alif melambangkan [a] di suku kata tertutup untuk membezakannya daripada pepet yang tidak dilambangkan. **Ejaan DBP sudah mengandungi peraturan ini**, jadi pakej tidak perlu menjananya. Sembilan pasangan rujukan diuji, kesemuanya dibezakan dengan betul.
290
+
291
+ Jenis pertama pula tidak boleh diselesaikan oleh mana-mana peraturan. Itulah sebab `rumi` melaporkan semua calon dan enggan memilih: bagi homograf hakiki, DBP sendiri menyatakan kekaburan itu dibenarkan dan hanya konteks ayat boleh menyelesaikannya.
292
+
293
+ `--homograf` menandakan perkataan yang ejaan Jawinya dikongsi, supaya penulis tahu sebelum menghantar bahawa pembaca akan bergantung pada konteks:
294
+
295
+ ```
296
+ $ npx prpm-dbp jawi nota.txt --homograf
297
+ اݢام ايت ساتو دان بوروڠ تربڠ.
298
+ homograf, ejaan Jawi dikongsi (3): اݢام=agama/igama, ساتو=satu/sato, بوروڠ=burung/borong
299
+ ```
300
+
275
301
  ## Rantai kepercayaan
276
302
 
277
303
  Setiap ejaan Jawi yang dikeluarkan membawa asal-usulnya. Jangan runtuhkan lapisan ini.
package/bin/cli.js CHANGED
@@ -36,6 +36,7 @@ const BANTUAN = `prpm-dbp — semak Bahasa Melayu dan Jawi terhadap PRPM DBP
36
36
  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
+ --homograf tandakan perkataan yang ejaan Jawinya dikongsi perkataan lain
39
40
 
40
41
  Cache: ${CACHE_PATH}
41
42
  `
@@ -51,6 +52,7 @@ function huraiArgs(argv) {
51
52
  else if (a === '--cepat') pilihan.cepat = true
52
53
  else if (a === '--pedoman') pilihan.pedoman = true
53
54
  else if (a === '--luar-talian') pilihan.luarTalian = true
55
+ else if (a === '--homograf') pilihan.homograf = true
54
56
  else if (a === '--jenis') pilihan.jenis = argv[++i]
55
57
  else if (a === '--had') pilihan.had = Number(argv[++i])
56
58
  else if (a === '--delay') pilihan.delay = Number(argv[++i])
@@ -164,6 +166,7 @@ async function main() {
164
166
  ...opts,
165
167
  cubaDasar: !pilihan.cepat,
166
168
  janaImbuhan: pilihan.pedoman,
169
+ semakHomograf: pilihan.homograf,
167
170
  })
168
171
  await saveCache(cache)
169
172
  if (pilihan.json) { keluar(h); break }
@@ -183,6 +186,12 @@ async function main() {
183
186
  h.dijanaPedoman.map((x) => `${x.kata}=${x.jawi} [${x.rujukan}]`).join(', '),
184
187
  )
185
188
  }
189
+ if (h.homograf?.length) {
190
+ console.error(
191
+ `homograf, ejaan Jawi dikongsi (${h.homograf.length}): ` +
192
+ h.homograf.map((x) => `${x.jawi}=${x.dikongsi.join('/')}`).join(', '),
193
+ )
194
+ }
186
195
  if (h.akronim.length) {
187
196
  console.error(`akronim, dikekalkan Rumi (${h.akronim.length}): ${h.akronim.join(', ')}`)
188
197
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prpm-dbp",
3
- "version": "0.11.1",
3
+ "version": "0.13.0",
4
4
  "description": "Semak ejaan Bahasa Melayu dan Jawi terhadap PRPM Dewan Bahasa dan Pustaka. CLI dan skill untuk AI agent.",
5
5
  "keywords": [
6
6
  "bahasa-melayu",
package/src/pedoman.js CHANGED
@@ -185,6 +185,21 @@ export function imbuhAkhiranI(jawiDasar, rumiDasar) {
185
185
  return { jawi: jawiDasar + YA, rujukan: 'Pedoman DBP §13.11(iii)' }
186
186
  }
187
187
 
188
+ /**
189
+ * §13.9 — Akhiran -kan ditulis kaf-nun, dilekatkan terus tanpa perubahan
190
+ * pada kata dasar.
191
+ *
192
+ * Nota panduan sekolah sering menyatakan "huruf alif ditambah semula apabila
193
+ * menerima imbuhan". Itu terlalu menyeluruh: diuji terhadap data sebenar,
194
+ * suka سوک menjadi sukakan سوککن dan lukanya لوکڽ, tanpa alif. Penambahan
195
+ * alif berlaku bagi -an dan -i sahaja, menurut Catatan §13.10(ii) dan
196
+ * §13.11(i).
197
+ */
198
+ export function imbuhAkhiranKan(jawiDasar) {
199
+ if (!jawiDasar) return null
200
+ return { jawi: jawiDasar + 'کن', rujukan: 'Pedoman DBP §13.9' }
201
+ }
202
+
188
203
  /**
189
204
  * §15.1 — Kata ganti singkat ditulis serangkai dengan kata di depannya.
190
205
  * ku -> kaf-wau mu -> mim-wau nya -> nya
package/src/tukar.js CHANGED
@@ -14,6 +14,7 @@ import { cariDataset, muatDataset } from './dataset.js'
14
14
  import { cariNama, mungkinAkronim, mungkinNamaKhas } from './nama.js'
15
15
  import {
16
16
  imbuhAkhiranAn,
17
+ imbuhAkhiranKan,
17
18
  imbuhAkhiranI,
18
19
  imbuhAwalan,
19
20
  imbuhGanti,
@@ -113,6 +114,11 @@ async function janaIkutPedoman(kata, cache, opts, gunaDataset) {
113
114
  if (hasil) return hasil
114
115
  }
115
116
 
117
+ if (kata.endsWith('kan') && kata.length > 5) {
118
+ const hasil = imbuhAkhiranKan(await ejaan(kata.slice(0, -3)))
119
+ if (hasil) return hasil
120
+ }
121
+
116
122
  if (kata.endsWith('an') && kata.length > 4) {
117
123
  const dasar = kata.slice(0, -2)
118
124
  const hasil = imbuhAkhiranAn(await ejaan(dasar), dasar)
@@ -212,7 +218,13 @@ export function pilihCalon(masuk) {
212
218
  * Perkataan yang tidak disahkan dibalut penanda, tidak pernah direka.
213
219
  */
214
220
  export async function keJawi(teks, cache, opts = {}) {
215
- const { penanda = ['«', '»'], cubaDasar = true, gunaDataset = true, janaImbuhan = false } = opts
221
+ const {
222
+ penanda = ['«', '»'],
223
+ cubaDasar = true,
224
+ gunaDataset = true,
225
+ janaImbuhan = false,
226
+ semakHomograf = false,
227
+ } = opts
216
228
  const belumDisahkan = []
217
229
  const terbitanSah = []
218
230
  const dariDataset = []
@@ -306,6 +318,25 @@ export async function keJawi(teks, cache, opts = {}) {
306
318
  keluar.push(penanda[0] + cebis + penanda[1])
307
319
  }
308
320
 
321
+ // Amaran homograf. Ejaan Jawi yang dikongsi dua perkataan Rumi akan taksa
322
+ // apabila dibaca semula. Menurut panduan DBP, homograf HAKIKI (kongsi
323
+ // vokal wau/ya, kata Melayu lawan kata asing, bentuk berimbuhan) memang
324
+ // dibenarkan kekal dan tidak dibezakan. Jadi ini bukan ralat: ia maklumat
325
+ // supaya penulis tahu konteks ayat perlu menanggung beban perbezaan itu.
326
+ const homograf = []
327
+ if (semakHomograf) {
328
+ const songsang = petaSongsang(cache, await muatDataset())
329
+ for (const [k, cebis] of bahagian.entries()) {
330
+ if (k % 2 === 0) continue
331
+ const hasil = keluar[k]
332
+ if (!hasil || hasil.startsWith(penanda[0])) continue
333
+ const pilihan = pilihCalon(songsang.get(normalisasiJawi(hasil)))
334
+ if (pilihan && pilihan.calon.length > 1) {
335
+ homograf.push({ kata: cebis, jawi: hasil, dikongsi: pilihan.calon })
336
+ }
337
+ }
338
+ }
339
+
309
340
  // Pedoman DBP §14.1: kata sendi di dan ke bercantum dengan kata berikutnya.
310
341
  // Dibuat sebagai lulusan kedua supaya logik carian di atas kekal mudah.
311
342
  const sendiDicantum = []
@@ -331,6 +362,7 @@ export async function keJawi(teks, cache, opts = {}) {
331
362
  dariDataset,
332
363
  dariNama,
333
364
  kataUlang,
365
+ homograf,
334
366
  dijanaPedoman,
335
367
  namaKhas: [...new Set(namaKhas)],
336
368
  akronim: [...new Set(akronim)],