prpm-dbp 0.5.0 → 0.6.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
@@ -144,6 +144,21 @@ Kata sendi **tidak** dicantum apabila kata berikutnya belum disahkan, kerana has
144
144
 
145
145
  Pakej tidak perlu memilih antara dua ini. DBP sudah memilih, dan pilihan itu terkandung dalam ejaan yang dipulangkan PRPM. Ia didokumenkan supaya tiada siapa "membetulkan" `صلاة` kepada ejaan fonetik Melayu kemudian hari.
146
146
 
147
+ **§11.6 — alif maqsurah dikekalkan.** `takwa` تقوى, `fatwa` فتوى, `Musa`, `Isa`. Ia diganti dengan alif hanya apabila dibentuk kata terbitan: `ketakwaan`, `memfatwakan`, `pendakwaan`. Perbezaan itu membawa maklumat, jadi `ى` **tidak pernah dilipat** semasa perbandingan. Melipatnya akan membuat `تقوي` yang salah dibandingkan sama dengan `تقوى` yang betul.
148
+
149
+ **§10 — enam belas perkataan dikecualikan daripada aturan pola:** `ada`, `apa`, `beta`, `bin`, `binti`, `dari`, `daripada`, `ini`, `itu`, `jika`, `kepada`, `kita`, `lima`, `maka`, `pada`, `demikian`. Ejaannya mesti datang daripada kamus dan tidak boleh dibetulkan oleh mana-mana peraturan pola.
150
+
151
+ **§17.1 — kata ulang penuh menggunakan angka dua Arab `٢`**, bukan sempang dan bukan kata dasar ditulis dua kali.
152
+
153
+ ```
154
+ Cikgu-cikgu -> چيقݢو٢
155
+ murid-murid -> موريد٢
156
+ ```
157
+
158
+ Kata dasar dicari dalam kamus seperti biasa, jadi ejaannya tetap datang daripada sumber. Hanya penambahan `٢` itu dijana oleh peraturan. Peraturan ini simetri: `چيقݢو٢` dibaca semula sebagai `cikgu-cikgu`.
159
+
160
+ §17.2 mengecualikan kata ulang yang **berubah bentuk** (berimbuhan atau berentak) seperti `bermaaf-maafan`. Di situ sempang kekal dan peraturan `٢` tidak terpakai, jadi padanan menuntut kedua-dua bahagian benar-benar serupa.
161
+
147
162
  ## Rantai kepercayaan
148
163
 
149
164
  Setiap ejaan Jawi yang dikeluarkan membawa asal-usulnya. Jangan runtuhkan lapisan ini.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prpm-dbp",
3
- "version": "0.5.0",
3
+ "version": "0.6.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/kamus.js CHANGED
@@ -187,8 +187,7 @@ export async function cariKamus(kata, { timeout = 25000 } = {}) {
187
187
  const LIPATAN = new Map([
188
188
  ['ك', 'ک'], // ك Arabic Kaf -> ک Keheh
189
189
  ['ڪ', 'ک'], // ڪ Swash Kaf -> ک Keheh
190
- ['ی', 'ي'], // ی Farsi Yeh -> ي Arabic Yeh
191
- ['ى', 'ي'], // ى Alef Maksura -> ي Arabic Yeh
190
+ ['ی', 'ي'], // ی Farsi Yeh -> ي Arabic Yeh (varian bentuk semata-mata)
192
191
  ['ڬ', 'ݢ'], // ڬ Gaf varian -> ݢ Gaf Jawi Melayu
193
192
  ['ە', 'ه'], // ە Ae -> ه Ha
194
193
  ['ہ', 'ه'], // ہ Heh Goal -> ه Ha
@@ -204,6 +203,14 @@ export function normalisasiJawi(teks) {
204
203
  .trim()
205
204
  }
206
205
 
206
+ // SENGAJA TIDAK DILIPAT: ى (U+0649, alif maqsurah) kepada ي (U+064A).
207
+ // Pedoman DBP §11.6: alif maqsurah dalam ejaan asal Arab DIKEKALKAN dalam
208
+ // ejaan Jawi — takwa تقوى, fatwa فتوى, Musa, Isa. Melipatnya akan membuat
209
+ // تقوي (salah) dibandingkan sama dengan تقوى (betul), lalu menyembunyikan
210
+ // kesilapan sebenar. §11.6 juga menyatakan huruf itu diganti dengan alif
211
+ // apabila dibentuk kata terbitan (ketakwaan, memfatwakan), jadi perbezaan
212
+ // itu memang membawa maklumat.
213
+
207
214
  // SENGAJA TIDAK DILIPAT: پ (U+067E, pe Parsi) kepada ڽ (U+06BD, nya).
208
215
  // Sesetengah enjin transliterasi negeri mengeluarkan پ di tempat ڽ, tetapi
209
216
  // itu huruf yang SALAH, bukan varian bentuk. Melipatnya akan menyembunyikan
package/src/pedoman.js CHANGED
@@ -82,3 +82,58 @@ export function pisahSendi(jawi) {
82
82
  }
83
83
  return calon
84
84
  }
85
+
86
+ /**
87
+ * §10 — Senarai perkataan yang dikecualikan daripada beberapa aturan
88
+ * pengejaan biasa. Ejaannya tidak menurut pola suku kata, jadi ia mesti
89
+ * datang daripada kamus dan tidak boleh "dibetulkan" oleh mana-mana
90
+ * peraturan pola.
91
+ */
92
+ export const KECUALI_POLA = new Set([
93
+ 'ada', 'apa', 'beta', 'bin', 'binti', 'dari', 'daripada', 'ini', 'itu',
94
+ 'jika', 'kepada', 'kita', 'lima', 'maka', 'pada', 'demikian',
95
+ ])
96
+
97
+ /**
98
+ * §17.1 — Kata ulang penuh ditulis dengan tanda angka dua Arab (٢),
99
+ * bukan dengan sempang seperti dalam Rumi.
100
+ * rama-rama -> رام٢
101
+ * §17.2 — Apabila bahagian yang diulang berubah bentuk (kata ulang
102
+ * berimbuhan atau berentak), tanda sempang digunakan.
103
+ */
104
+ export const ANGKA_DUA = '\u0662'
105
+
106
+ /**
107
+ * §11.6 — Alif maqsurah dalam ejaan asal Arab dikekalkan (takwa تقوى,
108
+ * fatwa فتوى, Musa, Isa), TETAPI diganti dengan alif apabila dibentuk kata
109
+ * terbitan dengan akhiran atau partikel (ketakwaan, memfatwakan,
110
+ * pendakwaan). Kerana itu ia tidak boleh dilipat semasa perbandingan.
111
+ */
112
+ export const NOTA_MAQSURAH = 'Pedoman DBP §11.6'
113
+
114
+ /**
115
+ * §17.1 — Kata ulang PENUH ditulis sebagai kata dasar diikuti angka dua
116
+ * Arab. Kata dasar tidak ditulis dua kali.
117
+ *
118
+ * kanak-kanak -> کانق٢ murid-murid -> موريد٢
119
+ *
120
+ * §17.2 — Ini TIDAK terpakai apabila bahagian yang diulang berubah bentuk
121
+ * (kata ulang berimbuhan atau berentak). Di situ sempang digunakan. Kerana
122
+ * itu padanan di bawah menuntut kedua-dua bahagian benar-benar serupa.
123
+ */
124
+ export function pisahKataUlang(kata) {
125
+ const m = /^(.+)-\1$/i.exec(String(kata))
126
+ return m ? m[1] : null
127
+ }
128
+
129
+ export function binaKataUlang(jawiDasar) {
130
+ if (!jawiDasar) return null
131
+ return { jawi: jawiDasar + ANGKA_DUA, rujukan: 'Pedoman DBP §17.1' }
132
+ }
133
+
134
+ /** Songsangan §17.1: kata dasar + ٢ menjadi kata dasar berulang penuh. */
135
+ export function pisahUlangJawi(jawi) {
136
+ if (!jawi.endsWith(ANGKA_DUA)) return null
137
+ const dasar = jawi.slice(0, -ANGKA_DUA.length)
138
+ return dasar ? dasar : null
139
+ }
package/src/tukar.js CHANGED
@@ -12,7 +12,14 @@ import { lookup } from './prpm.js'
12
12
  import { normalisasiJawi } from './kamus.js'
13
13
  import { cariDataset, muatDataset } from './dataset.js'
14
14
  import { cariNama, mungkinNamaKhas } from './nama.js'
15
- import { bolehCantumSendi, cantumSendi, pisahSendi } from './pedoman.js'
15
+ import {
16
+ binaKataUlang,
17
+ bolehCantumSendi,
18
+ cantumSendi,
19
+ pisahKataUlang,
20
+ pisahSendi,
21
+ pisahUlangJawi,
22
+ } from './pedoman.js'
16
23
 
17
24
  // Baris tanda vokal Arab (fathah, kasrah, dammah, sukun, syaddah, tanwin,
18
25
  // alef superscript). Jawi Melayu TIDAK menggunakannya; teks al-Quran dan
@@ -134,6 +141,7 @@ export async function keJawi(teks, cache, opts = {}) {
134
141
  const adaEntriTanpaJawi = []
135
142
  const dariNama = []
136
143
  const namaKhas = []
144
+ const kataUlang = []
137
145
  const bahagian = teks.split(PECAH_RUMI)
138
146
  const keluar = []
139
147
 
@@ -159,6 +167,21 @@ export async function keJawi(teks, cache, opts = {}) {
159
167
  }
160
168
  }
161
169
 
170
+ // Pedoman DBP §17.1: kata ulang penuh ialah kata dasar + angka dua Arab.
171
+ // Kata dasar dicari seperti biasa, jadi ejaannya tetap datang daripada
172
+ // kamus; hanya penambahan ٢ itu yang dijana oleh peraturan.
173
+ const dasarUlang = pisahKataUlang(cebis)
174
+ if (dasarUlang) {
175
+ const recDasar = await lookup(dasarUlang.toLowerCase(), cache, opts)
176
+ const jawiDasar = recDasar?.jawi ?? (gunaDataset ? (await cariDataset(dasarUlang.toLowerCase()))?.jawi : null)
177
+ const ulang = binaKataUlang(jawiDasar)
178
+ if (ulang) {
179
+ kataUlang.push({ kata: cebis, dasar: dasarUlang, jawi: ulang.jawi, rujukan: ulang.rujukan })
180
+ keluar.push(ulang.jawi)
181
+ continue
182
+ }
183
+ }
184
+
162
185
  // Nama khas yang pengguna sudah sahkan sendiri.
163
186
  const nm = await cariNama(cebis)
164
187
  if (nm?.jawi) {
@@ -210,6 +233,7 @@ export async function keJawi(teks, cache, opts = {}) {
210
233
  disahkanDbp: dbp.length,
211
234
  dariDataset,
212
235
  dariNama,
236
+ kataUlang,
213
237
  namaKhas: [...new Set(namaKhas)],
214
238
  adaEntriTanpaJawi: [...new Set(adaEntriTanpaJawi)],
215
239
  belumDisahkan: [...new Set(belumDisahkan)],
@@ -270,6 +294,16 @@ export function keRumi(teksJawi, cache, { penanda = ['«', '»'], dataset = null
270
294
  }
271
295
 
272
296
  if (!padanan) {
297
+ // Pedoman DBP §17.1 songsang: kata dasar + ٢ menjadi kata berulang.
298
+ const dasarUlang = pisahUlangJawi(bahagian[i])
299
+ if (dasarUlang) {
300
+ const jumpaUlang = peta.get(normalisasiJawi(dasarUlang))
301
+ if (jumpaUlang?.length === 1) {
302
+ keluar.push(jumpaUlang[0] + '-' + jumpaUlang[0])
303
+ continue
304
+ }
305
+ }
306
+
273
307
  // Pedoman DBP §14.1 songsang: bentuk bercantum seperti دمکة tiada
274
308
  // dalam kamus, jadi cuba pisahkan kata sendi di hadapan.
275
309
  let dipisah = null