prpm-dbp 0.3.0 → 0.4.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 +1 -0
- package/SKILL.md +25 -0
- package/bin/cli.js +20 -0
- package/package.json +1 -1
- package/src/nama.js +70 -0
- package/src/tukar.js +19 -2
package/README.md
CHANGED
|
@@ -128,6 +128,7 @@ Pengguna kemudian menarik versi baharu dengan `npx skills update`.
|
|
|
128
128
|
| `jawi <fail>` | tukar teks Rumi ke Jawi ikut ejaan DBP |
|
|
129
129
|
| `rumi <fail>` | tukar teks Jawi ke Rumi dari cache |
|
|
130
130
|
| `import-tsv <fail>` | import dataset rumi/jawi tambahan |
|
|
131
|
+
| `nama <rumi> <jawi>` | sahkan satu nama khas |
|
|
131
132
|
| `banding <kata...>` | silang semak PRPM lawan KamusDBP |
|
|
132
133
|
| `cache` | status cache, `--buang <kata>` untuk padam satu |
|
|
133
134
|
|
package/SKILL.md
CHANGED
|
@@ -91,6 +91,31 @@ Sesetengah entri memulangkan **dua perkataan Jawi untuk satu perkataan Rumi**: `
|
|
|
91
91
|
|
|
92
92
|
**Teks Arab yang dipetik dibiarkan sepenuhnya.** Jawi Melayu tidak menggunakan baris tanda vokal, jadi kehadiran fathah, kasrah, dammah, sukun atau tanwin bermakna potongan itu Arab, bukan Jawi. Ayat al-Quran dan hadis dalam dokumen Melayu lulus tanpa disentuh dan dilaporkan dalam medan `arab`.
|
|
93
93
|
|
|
94
|
+
## Nama khas
|
|
95
|
+
|
|
96
|
+
Diuji terhadap PRPM: **nama tokoh dan nama surah tiada sumber berwibawa.** `muhammad`, `ibrahim`, `musa`, `umar`, `aisyah`, `khadijah` semuanya tiada entri. Nama surah seperti `fatihah`, `baqarah`, `yasin` ada entri tetapi tanpa ejaan Jawi. Dataset komuniti meliputi nama tempat (`mekah` مکة, `mesir` مسير, `pahang` ڤاهڠ) tetapi bukan nama orang.
|
|
97
|
+
|
|
98
|
+
Alat ini **tidak mereka ejaan nama**. Sebaliknya:
|
|
99
|
+
|
|
100
|
+
- Perkataan berhuruf besar di **tengah** ayat dikira nama khas dan dilaporkan dalam medan `namaKhas`, berasingan daripada `belumDisahkan`. Nama bukan salah eja.
|
|
101
|
+
- Sahkan sekali, guna selamanya:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
npx prpm-dbp nama Muhammad <ejaan-jawi>
|
|
105
|
+
npx prpm-dbp nama # senaraikan yang sudah disahkan
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Ejaan yang disimpan begini ditandakan datang daripada pengguna, bukan daripada DBP. Sahkan dengan guru atau rujukan bertulis dahulu.
|
|
109
|
+
|
|
110
|
+
**Jangan keliru antara dua benda:**
|
|
111
|
+
|
|
112
|
+
| | Layanan |
|
|
113
|
+
|---|---|
|
|
114
|
+
| Petikan bahasa Arab (ayat, doa, hadis) | kekal Arab, tidak disentuh |
|
|
115
|
+
| Nama khas dalam ayat Melayu | **ditulis dalam Jawi**, bukan dikekalkan |
|
|
116
|
+
|
|
117
|
+
Bagi nama berasal Arab, ejaan Jawinya memang ejaan Arab asal, jadi ia nampak seperti dikekalkan. Bagi nama bukan Arab ia ditransliterasi penuh: `Kuala Lumpur` menjadi `کوالا لومڤور`.
|
|
118
|
+
|
|
94
119
|
## Rantai kepercayaan
|
|
95
120
|
|
|
96
121
|
Setiap ejaan Jawi yang dikeluarkan membawa asal-usulnya. Jangan runtuhkan lapisan ini.
|
package/bin/cli.js
CHANGED
|
@@ -6,6 +6,7 @@ import { pasangSkill } from '../src/install.js'
|
|
|
6
6
|
import { banding, cariKamus } from '../src/kamus.js'
|
|
7
7
|
import { keJawi, keRumi } from '../src/tukar.js'
|
|
8
8
|
import { huraiTsv, muatDataset, simpanDataset } from '../src/dataset.js'
|
|
9
|
+
import { NAMA_PATH, muatNama, tambahNama } from '../src/nama.js'
|
|
9
10
|
import {
|
|
10
11
|
CACHE_PATH,
|
|
11
12
|
auditJawi,
|
|
@@ -26,6 +27,8 @@ const BANTUAN = `prpm-dbp — semak Bahasa Melayu dan Jawi terhadap PRPM DBP
|
|
|
26
27
|
npx prpm-dbp jawi <fail.txt> tukar teks Rumi ke Jawi ikut ejaan DBP
|
|
27
28
|
npx prpm-dbp rumi <fail.txt> tukar teks Jawi ke Rumi dari cache
|
|
28
29
|
npx prpm-dbp import-tsv <fail.tsv> import dataset rumi<TAB>jawi (dilabel bukan DBP)
|
|
30
|
+
npx prpm-dbp nama <rumi> <jawi> sahkan satu nama khas (tokoh, surah, tempat)
|
|
31
|
+
npx prpm-dbp nama senaraikan nama khas yang disahkan
|
|
29
32
|
npx prpm-dbp banding <kata...> silang semak PRPM lawan KamusDBP
|
|
30
33
|
npx prpm-dbp cache [--buang <kata>] status cache
|
|
31
34
|
npx prpm-dbp install [--link] pasang sebagai Claude Code skill
|
|
@@ -160,6 +163,12 @@ async function main() {
|
|
|
160
163
|
h.adaEntriTanpaJawi.join(', '),
|
|
161
164
|
)
|
|
162
165
|
}
|
|
166
|
+
if (h.namaKhas.length) {
|
|
167
|
+
console.error(
|
|
168
|
+
`nama khas belum disahkan (${h.namaKhas.length}): ${h.namaKhas.join(', ')}\n` +
|
|
169
|
+
` sahkan dengan: npx prpm-dbp nama <rumi> <jawi>`,
|
|
170
|
+
)
|
|
171
|
+
}
|
|
163
172
|
if (h.terbitanSah.length) {
|
|
164
173
|
console.error(
|
|
165
174
|
`terbitan sah tanpa ejaan Jawi (${h.terbitanSah.length}): ` +
|
|
@@ -183,6 +192,17 @@ async function main() {
|
|
|
183
192
|
break
|
|
184
193
|
}
|
|
185
194
|
|
|
195
|
+
case 'nama': {
|
|
196
|
+
if (baki.length >= 2) {
|
|
197
|
+
const rec = await tambahNama(baki[0], baki[1], { jenis: pilihan.jenis ?? null })
|
|
198
|
+
keluar({ disimpan: rec, fail: NAMA_PATH, nota: 'Ejaan ini datang dari anda, bukan dari DBP.' })
|
|
199
|
+
break
|
|
200
|
+
}
|
|
201
|
+
const peta = await muatNama()
|
|
202
|
+
keluar({ fail: NAMA_PATH, jumlah: Object.keys(peta).length, nama: peta })
|
|
203
|
+
break
|
|
204
|
+
}
|
|
205
|
+
|
|
186
206
|
case 'import-tsv': {
|
|
187
207
|
if (!baki[0]) throw new Error('perlu fail TSV: rumi<TAB>jawi setiap baris')
|
|
188
208
|
const { peta, dilangkau, jumlah } = huraiTsv(await readFile(baki[0], 'utf8'))
|
package/package.json
CHANGED
package/src/nama.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simpanan nama khas yang disahkan pengguna.
|
|
3
|
+
*
|
|
4
|
+
* Kenapa ini wujud: nama tokoh dan nama surah tiada sumber berwibawa yang
|
|
5
|
+
* boleh dicapai. Diuji terhadap PRPM, muhammad, ibrahim, musa, umar, aisyah
|
|
6
|
+
* dan khadijah semuanya TIADA entri. Dataset komuniti pula meliputi nama
|
|
7
|
+
* tempat (مکة, مسير, ڤاهڠ) tetapi bukan nama orang atau nama surah.
|
|
8
|
+
*
|
|
9
|
+
* Alat ini TIDAK mereka ejaan nama. Ia menyimpan apa yang pengguna sahkan
|
|
10
|
+
* sendiri, dengan sumber dicatat, supaya kerja pengesahan itu dibuat sekali
|
|
11
|
+
* sahaja dan boleh diaudit kemudian.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { mkdir, readFile, rename, writeFile } from 'node:fs/promises'
|
|
15
|
+
import { dirname, join } from 'node:path'
|
|
16
|
+
|
|
17
|
+
import { CACHE_PATH } from './prpm.js'
|
|
18
|
+
|
|
19
|
+
export const NAMA_PATH = process.env.PRPM_NAMA ?? join(dirname(CACHE_PATH), 'nama-khas.json')
|
|
20
|
+
|
|
21
|
+
let dimuat = null
|
|
22
|
+
|
|
23
|
+
export async function muatNama() {
|
|
24
|
+
if (dimuat !== null) return dimuat
|
|
25
|
+
try {
|
|
26
|
+
dimuat = JSON.parse(await readFile(NAMA_PATH, 'utf8'))
|
|
27
|
+
} catch {
|
|
28
|
+
dimuat = {}
|
|
29
|
+
}
|
|
30
|
+
return dimuat
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function simpanNama(peta) {
|
|
34
|
+
await mkdir(dirname(NAMA_PATH), { recursive: true })
|
|
35
|
+
const tmp = `${NAMA_PATH}.tmp`
|
|
36
|
+
await writeFile(tmp, JSON.stringify(peta, null, 1), 'utf8')
|
|
37
|
+
await rename(tmp, NAMA_PATH)
|
|
38
|
+
dimuat = peta
|
|
39
|
+
return NAMA_PATH
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export async function tambahNama(rumi, jawi, { sumber = 'pengguna', jenis = null } = {}) {
|
|
43
|
+
const peta = { ...(await muatNama()) }
|
|
44
|
+
const kunci = String(rumi).trim().toLowerCase()
|
|
45
|
+
if (!kunci || !jawi) throw new Error('perlu rumi dan jawi')
|
|
46
|
+
peta[kunci] = { rumi: kunci, jawi: String(jawi).trim(), sumber, jenis }
|
|
47
|
+
await simpanNama(peta)
|
|
48
|
+
return peta[kunci]
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export async function cariNama(kata) {
|
|
52
|
+
const peta = await muatNama()
|
|
53
|
+
const rec = peta[String(kata).trim().toLowerCase()]
|
|
54
|
+
if (!rec) return null
|
|
55
|
+
return { ...rec, dijumpai: true, disahkanDbp: false }
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Teka sama ada perkataan itu nama khas.
|
|
60
|
+
*
|
|
61
|
+
* Huruf besar di TENGAH ayat ialah isyarat yang munasabah dalam teks Melayu.
|
|
62
|
+
* Ia sengaja tidak digunakan pada perkataan pertama ayat, kerana di situ
|
|
63
|
+
* huruf besar tidak bermakna apa-apa.
|
|
64
|
+
*/
|
|
65
|
+
export function mungkinNamaKhas(kata, sebelumnya) {
|
|
66
|
+
if (!/^[A-Z]/.test(kata)) return false
|
|
67
|
+
// Awal teks, atau selepas noktah, tanda soal, tanda seru, atau baris baru.
|
|
68
|
+
const awalAyat = !sebelumnya || /[.!?:\n]\s*$/.test(sebelumnya)
|
|
69
|
+
return !awalAyat
|
|
70
|
+
}
|
package/src/tukar.js
CHANGED
|
@@ -11,6 +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
15
|
|
|
15
16
|
// Baris tanda vokal Arab (fathah, kasrah, dammah, sukun, syaddah, tanwin,
|
|
16
17
|
// alef superscript). Jawi Melayu TIDAK menggunakannya; teks al-Quran dan
|
|
@@ -130,6 +131,8 @@ export async function keJawi(teks, cache, opts = {}) {
|
|
|
130
131
|
const dariDataset = []
|
|
131
132
|
const dbp = []
|
|
132
133
|
const adaEntriTanpaJawi = []
|
|
134
|
+
const dariNama = []
|
|
135
|
+
const namaKhas = []
|
|
133
136
|
const bahagian = teks.split(PECAH_RUMI)
|
|
134
137
|
const keluar = []
|
|
135
138
|
|
|
@@ -155,6 +158,14 @@ export async function keJawi(teks, cache, opts = {}) {
|
|
|
155
158
|
}
|
|
156
159
|
}
|
|
157
160
|
|
|
161
|
+
// Nama khas yang pengguna sudah sahkan sendiri.
|
|
162
|
+
const nm = await cariNama(cebis)
|
|
163
|
+
if (nm?.jawi) {
|
|
164
|
+
dariNama.push({ kata: cebis, jawi: nm.jawi })
|
|
165
|
+
keluar.push(nm.jawi)
|
|
166
|
+
continue
|
|
167
|
+
}
|
|
168
|
+
|
|
158
169
|
if (cubaDasar) {
|
|
159
170
|
const dasar = await cariDasar(cebis.toLowerCase(), cache, opts)
|
|
160
171
|
if (dasar) {
|
|
@@ -166,7 +177,11 @@ export async function keJawi(teks, cache, opts = {}) {
|
|
|
166
177
|
}
|
|
167
178
|
}
|
|
168
179
|
|
|
169
|
-
|
|
180
|
+
// Nama khas yang belum disahkan bukan salah eja. Ia dilaporkan
|
|
181
|
+
// berasingan supaya pengguna tahu apa yang perlu disahkan sekali, dan
|
|
182
|
+
// bukan mengulang menyemak ejaan yang memang betul.
|
|
183
|
+
if (mungkinNamaKhas(cebis, bahagian[i - 1])) namaKhas.push(cebis)
|
|
184
|
+
else belumDisahkan.push(cebis)
|
|
170
185
|
keluar.push(penanda[0] + cebis + penanda[1])
|
|
171
186
|
}
|
|
172
187
|
|
|
@@ -174,10 +189,12 @@ export async function keJawi(teks, cache, opts = {}) {
|
|
|
174
189
|
teks: keluar.join(''),
|
|
175
190
|
disahkanDbp: dbp.length,
|
|
176
191
|
dariDataset,
|
|
192
|
+
dariNama,
|
|
193
|
+
namaKhas: [...new Set(namaKhas)],
|
|
177
194
|
adaEntriTanpaJawi: [...new Set(adaEntriTanpaJawi)],
|
|
178
195
|
belumDisahkan: [...new Set(belumDisahkan)],
|
|
179
196
|
terbitanSah,
|
|
180
|
-
lengkap: belumDisahkan.length === 0 && terbitanSah.length === 0,
|
|
197
|
+
lengkap: belumDisahkan.length === 0 && terbitanSah.length === 0 && namaKhas.length === 0,
|
|
181
198
|
// Sepenuhnya DBP bermakna setiap perkataan datang dari sumber rasmi.
|
|
182
199
|
semuanyaDbp: dariDataset.length === 0 && belumDisahkan.length === 0 && terbitanSah.length === 0,
|
|
183
200
|
}
|