diff --git a/locales/en.json b/locales/en.json
index 2c8b679..813605b 100644
--- a/locales/en.json
+++ b/locales/en.json
@@ -2183,6 +2183,94 @@
"save": "Save Expense",
"title": "Expenses",
"th_method": "Method"
+ },
+ "ar": {
+ "heading": "Area / Region Management",
+ "add": "Add New Area",
+ "list": "Area / Region Master List",
+ "th_desc": "Note / Description",
+ "th_cust_count": "Customer Count",
+ "th_col_count": "Collector Count",
+ "del": "Delete Area",
+ "name_req": "Area / Region Name *",
+ "ph_name": "Example: Sukamaju Village, Block C1-C10",
+ "ph_desc": "Example: Coverage area ODP-01 to ODP-05",
+ "save": "Save Area",
+ "edit": "Edit Area Data",
+ "title": "Area Management",
+ "th_name": "Area / Region Name"
+ },
+ "rt": {
+ "list": "MikroTik Router List",
+ "view_grid": "Card Grid",
+ "script_isolir": "Isolir Script",
+ "add_now": "Add Router Now",
+ "add": "Add Router",
+ "pppoe_active": "PPPoE Active",
+ "live_detail": "Live Status Detail",
+ "setup_fw": "Isolir Firewall Setup",
+ "edit": "Edit Router",
+ "del": "Delete Router",
+ "live_title": "Router Live Detail"
+ },
+ "bc": {
+ "send_title": "Send Invoices Now (Manual — if Auto is OFF)",
+ "send": "Send Invoices Now",
+ "spintax": "Spintax Word Variation:",
+ "spintax_desc": "Use {Hi|Good Morning|Dear} so the message is auto-randomized & not treated as spam.",
+ "human_delay": "Human-like Typing Delay:",
+ "human_delay_desc": "Before sending, the system waits a moment (scaled to message length) to mimic human typing tempo.",
+ "random_batch": "Random Delay & Batching:",
+ "random_batch_desc": "Sent with dynamic random delays and automatic rest between batches.",
+ "isolir_date": "Isolir Date",
+ "opt_static": "Static",
+ "opt_random": "Random (anti-ban)",
+ "delay_sec": "Delay (seconds)",
+ "stop": "Stop"
+ },
+ "bk": {
+ "title": "Backup & Recovery",
+ "total": "Total Backups",
+ "files_saved": "Files stored",
+ "db_backup": "Database backup",
+ "set_backup": "Settings backup",
+ "size_used": "Capacity used",
+ "create": "Create New Backup",
+ "type": "Backup Type",
+ "opt_all": "All (Database + Settings)",
+ "opt_db": "Database Only",
+ "opt_set": "Settings Only",
+ "upload": "Upload & Restore Backup",
+ "pick_file": "Select Backup File (.db / .json)",
+ "auto_clean": "Automatic Cleanup",
+ "retention": "Retention (Days)",
+ "history": "Backup History",
+ "empty": "No backup files available"
+ },
+ "wt": {
+ "title": "WhatsApp Notification Template Settings",
+ "lbl_reminder": "Invoice Reminder Template (Monthly / Manual)",
+ "lbl_qris": "QRIS Invoice Template (Manual)",
+ "lbl_success": "Payment Success Template (Paid)",
+ "lbl_isolir": "Isolir Notice Template (Auto / Manual)",
+ "lbl_arrears": "Arrears Warning Template (approaching deactivation)",
+ "lbl_autopaid": "Auto-Paid Template (Rp 0 / auto_isolate=0)",
+ "lbl_autoinactive": "Auto-Deactivate Template (arrears)",
+ "lbl_autodelete": "Auto-Delete Template (4th month)",
+ "convert": "Convert to Meta Cloud API Format",
+ "save": "Save Template",
+ "tool_title": "Meta WhatsApp Cloud API Template Converter",
+ "pick_tmpl": "Select the Template to Convert to Meta Format:",
+ "opt_reminder": "Invoice Reminder Template",
+ "opt_qris": "QRIS Invoice Template",
+ "opt_success": "Payment Success Template",
+ "opt_isolir": "Isolir Notice Template",
+ "vars": "Parameter Order Variables (Components Payload):"
+ },
+ "md": {
+ "connecting": "Connecting to MikroTik...",
+ "fullscreen": "Fullscreen",
+ "exit": "Exit"
}
},
"tech": {
diff --git a/locales/id.json b/locales/id.json
index 93c01b3..8ea406c 100644
--- a/locales/id.json
+++ b/locales/id.json
@@ -2184,6 +2184,94 @@
"save": "Simpan Pengeluaran",
"title": "Pengeluaran",
"th_method": "Metode"
+ },
+ "ar": {
+ "heading": "Manajemen Area / Wilayah",
+ "add": "Tambah Area Baru",
+ "list": "Daftar Master Area / Wilayah",
+ "th_desc": "Keterangan / Deskripsi",
+ "th_cust_count": "Jumlah Pelanggan",
+ "th_col_count": "Jumlah Kolektor",
+ "del": "Hapus Area",
+ "name_req": "Nama Area / Wilayah *",
+ "ph_name": "Contoh: Kelurahan Sukamaju, Blok C1-C10",
+ "ph_desc": "Contoh: Wilayah jangkauan ODP-01 s/d ODP-05",
+ "save": "Simpan Area",
+ "edit": "Edit Data Area",
+ "title": "Manajemen Area",
+ "th_name": "Nama Area / Wilayah"
+ },
+ "rt": {
+ "list": "Daftar Router MikroTik",
+ "view_grid": "Grid Kartu",
+ "script_isolir": "Script Isolir",
+ "add_now": "Tambah Router Sekarang",
+ "add": "Tambah Router",
+ "pppoe_active": "PPPoE Aktif",
+ "live_detail": "Detail Live Status",
+ "setup_fw": "Setup Firewall Isolir",
+ "edit": "Edit Router",
+ "del": "Hapus Router",
+ "live_title": "Detail Live Router"
+ },
+ "bc": {
+ "send_title": "Kirim Tagihan Sekarang (Manual — bila Auto OFF)",
+ "send": "Kirim Tagihan Sekarang",
+ "spintax": "Variasi Kata Spintax:",
+ "spintax_desc": "Gunakan {Halo|Selamat Pagi|Yth.} agar isi pesan otomatis diacak & tidak dianggap spam.",
+ "human_delay": "Jeda Mengetik Manusiawi:",
+ "human_delay_desc": "Sebelum dikirim, sistem menunggu sejenak (menyesuaikan panjang pesan) meniru tempo menulis manusia.",
+ "random_batch": "Jeda Acak & Batching:",
+ "random_batch_desc": "Dikirim dengan jeda acak dinamis dan jeda istirahat otomatis antar batch.",
+ "isolir_date": "Tanggal Isolir",
+ "opt_static": "Statis",
+ "opt_random": "Random (anti-ban)",
+ "delay_sec": "Jeda (detik)",
+ "stop": "Hentikan"
+ },
+ "bk": {
+ "title": "Backup & Recovery",
+ "total": "Total Backup",
+ "files_saved": "File tersimpan",
+ "db_backup": "Backup database",
+ "set_backup": "Backup pengaturan",
+ "size_used": "Kapasitas digunakan",
+ "create": "Buat Backup Baru",
+ "type": "Tipe Backup",
+ "opt_all": "Semua (Database + Settings)",
+ "opt_db": "Database Saja",
+ "opt_set": "Settings Saja",
+ "upload": "Upload & Restore Backup",
+ "pick_file": "Pilih File Backup (.db / .json)",
+ "auto_clean": "Pembersihan Otomatis",
+ "retention": "Retensi (Hari)",
+ "history": "Riwayat Backup",
+ "empty": "Belum ada file backup tersedia"
+ },
+ "wt": {
+ "title": "Pengaturan Template Notifikasi WhatsApp",
+ "lbl_reminder": "Template Pengingat Tagihan (Bulanan / Manual)",
+ "lbl_qris": "Template Tagihan QRIS (Manual)",
+ "lbl_success": "Template Pembayaran Sukses (Lunas)",
+ "lbl_isolir": "Template Pemberitahuan Isolir (Otomatis / Manual)",
+ "lbl_arrears": "Template Peringatan Tunggakan (menuju nonaktif)",
+ "lbl_autopaid": "Template Auto-Lunas (Rp 0 / auto_isolate=0)",
+ "lbl_autoinactive": "Template Auto-Nonaktif (menunggak)",
+ "lbl_autodelete": "Template Auto-Hapus (bulan ke-4)",
+ "convert": "Konversi Format Meta Cloud API",
+ "save": "Simpan Template",
+ "tool_title": "Tool Konversi Template Meta WhatsApp Cloud API",
+ "pick_tmpl": "Pilih Template yang Akan Dikonversi ke Format Meta:",
+ "opt_reminder": "Template Pengingat Tagihan",
+ "opt_qris": "Template Tagihan QRIS",
+ "opt_success": "Template Pembayaran Sukses",
+ "opt_isolir": "Template Pemberitahuan Isolir",
+ "vars": "Daftar Variabel Urutan Parameter (Components Payload):"
+ },
+ "md": {
+ "connecting": "Menghubungkan ke MikroTik...",
+ "fullscreen": "Layar Penuh",
+ "exit": "Keluar"
}
},
"tech": {
diff --git a/views/admin/areas.ejs b/views/admin/areas.ejs
index 48d8c78..7a18e63 100644
--- a/views/admin/areas.ejs
+++ b/views/admin/areas.ejs
@@ -3,7 +3,7 @@
- Manajemen Area | <%= company %>
+ <%= t('admin.ar.title','Manajemen Area') %> | <%= company %>
@@ -15,11 +15,11 @@
- Manajemen Area / Wilayah
+ <%= t('admin.ar.heading','Manajemen Area / Wilayah') %>
<%= (typeof session !== 'undefined' && session.isCashier) ? t('admin.role.cashier', 'Kasir') : t('admin.role.admin', 'Admin') %>
- Tambah Area Baru
+ <%= t('admin.ar.add','Tambah Area Baru') %>
@@ -30,7 +30,7 @@
-
Daftar Master Area / Wilayah (<%= areas.length %>)
+ <%= t('admin.ar.list','Daftar Master Area / Wilayah') %> (<%= areas.length %>)
Area digunakan untuk mengelompokkan lokasi pelanggan dan wilayah tugas kolektor. Kolektor yang memiliki area tugas tertentu hanya akan mengelola pelanggan di area tersebut.
@@ -40,16 +40,16 @@
#
- Nama Area / Wilayah
- Keterangan / Deskripsi
- Jumlah Pelanggan
- Jumlah Kolektor
+ <%= t('admin.ar.th_name','Nama Area / Wilayah') %>
+ <%= t('admin.ar.th_desc','Keterangan / Deskripsi') %>
+ <%= t('admin.ar.th_cust_count','Jumlah Pelanggan') %>
+ <%= t('admin.ar.th_col_count','Jumlah Kolektor') %>
<%= t('common.actions','Aksi') %>
<% if (!areas || areas.length === 0) { %>
- Belum ada data area. Klik "Tambah Area Baru" untuk membuat area.
+ Belum ada data area. Klik "<%= t('admin.ar.add','Tambah Area Baru') %>" untuk membuat area.
<% } else { %>
<% areas.forEach((a, idx) => { %>
@@ -71,7 +71,7 @@
@@ -89,20 +89,20 @@
@@ -112,10 +112,10 @@
- Edit Data Area×
+ <%= t('admin.ar.edit','Edit Data Area') %>×
- Nama Area / Wilayah *
+ <%= t('admin.ar.name_req','Nama Area / Wilayah *') %>
diff --git a/views/admin/backup.ejs b/views/admin/backup.ejs
index 036e1b4..e64cec3 100644
--- a/views/admin/backup.ejs
+++ b/views/admin/backup.ejs
@@ -20,7 +20,7 @@
- Backup & Recovery
+ <%= t('admin.bk.title','Backup & Recovery') %>
@@ -46,42 +46,42 @@
-
Total Backup
+
<%= t('admin.bk.total','Total Backup') %>
<%= total %>
-
File tersimpan
+
<%= t('admin.bk.files_saved','File tersimpan') %>
Database
<%= backups.filter(b => b.type === 'database').length %>
-
Backup database
+
<%= t('admin.bk.db_backup','Backup database') %>
Settings
<%= backups.filter(b => b.type === 'settings').length %>
-
Backup pengaturan
+
<%= t('admin.bk.set_backup','Backup pengaturan') %>
Total Size
<%= (backups.reduce((sum, b) => sum + b.sizeKB, 0) / 1024).toFixed(1) %> MB
-
Kapasitas digunakan
+
<%= t('admin.bk.size_used','Kapasitas digunakan') %>
-
Buat Backup Baru
+
<%= t('admin.bk.create','Buat Backup Baru') %>
- Tipe Backup
+ <%= t('admin.bk.type','Tipe Backup') %>
- Semua (Database + Settings)
- Database Saja
- Settings Saja
+ <%= t('admin.bk.opt_all','Semua (Database + Settings)') %>
+ <%= t('admin.bk.opt_db','Database Saja') %>
+ <%= t('admin.bk.opt_set','Settings Saja') %>
@@ -93,11 +93,11 @@
-
Upload & Restore Backup
+
<%= t('admin.bk.upload','Upload & Restore Backup') %>
- Pilih File Backup (.db / .json)
+ <%= t('admin.bk.pick_file','Pilih File Backup (.db / .json)') %>
File .db (database) atau .json (settings)
@@ -110,11 +110,11 @@
-
Pembersihan Otomatis
+
<%= t('admin.bk.auto_clean','Pembersihan Otomatis') %>
- Retensi (Hari)
+ <%= t('admin.bk.retention','Retensi (Hari)') %>
Backup lebih lama dari periode ini akan dihapus
@@ -129,13 +129,13 @@
-
Riwayat Backup
+ <%= t('admin.bk.history','Riwayat Backup') %>
<% if (backups.length === 0) { %>
-
Belum ada file backup tersedia
+
<%= t('admin.bk.empty','Belum ada file backup tersedia') %>
<% } else { %>
diff --git a/views/admin/broadcast.ejs b/views/admin/broadcast.ejs
index 9427d22..a1aa872 100644
--- a/views/admin/broadcast.ejs
+++ b/views/admin/broadcast.ejs
@@ -59,9 +59,9 @@
Anti-Banned Aktif (Spintax + Jeda Mengetik)
- • Variasi Kata Spintax: Gunakan {Halo|Selamat Pagi|Yth.} agar isi pesan otomatis diacak & tidak dianggap spam.
- • Jeda Mengetik Manusiawi: Sebelum dikirim, sistem menunggu sejenak (menyesuaikan panjang pesan) meniru tempo menulis manusia.
- • Jeda Acak & Batching: Dikirim dengan jeda acak dinamis dan jeda istirahat otomatis antar batch.
+ • <%= t('admin.bc.spintax','Variasi Kata Spintax:') %> <%- t('admin.bc.spintax_desc','Gunakan {Halo|Selamat Pagi|Yth.} agar isi pesan otomatis diacak & tidak dianggap spam.') %>
+ • <%= t('admin.bc.human_delay','Jeda Mengetik Manusiawi:') %> <%= t('admin.bc.human_delay_desc','Sebelum dikirim, sistem menunggu sejenak (menyesuaikan panjang pesan) meniru tempo menulis manusia.') %>
+ • <%= t('admin.bc.random_batch','Jeda Acak & Batching:') %> <%= t('admin.bc.random_batch_desc','Dikirim dengan jeda acak dinamis dan jeda istirahat otomatis antar batch.') %>
@@ -114,11 +114,11 @@
-
Kirim Tagihan Sekarang (Manual — bila Auto OFF)
+
<%= t('admin.bc.send_title','Kirim Tagihan Sekarang (Manual — bila Auto OFF)') %>
-
Tanggal Isolir
+
<%= t('admin.bc.isolir_date','Tanggal Isolir') %>
<%= t('collector.filter_all','Semua') %>
<% (isolateDays||[]).forEach(function(d){ %>Tgl <%= d %> <% }); %>
@@ -131,11 +131,11 @@
Interval
- Statis
- Random (anti-ban)
+ <%= t('admin.bc.opt_static','Statis') %>
+ <%= t('admin.bc.opt_random','Random (anti-ban)') %>
-
Jeda (detik)
+
<%= t('admin.bc.delay_sec','Jeda (detik)') %>
Jeda acak (min–max detik)
@@ -150,8 +150,8 @@
- Hentikan
- Kirim Tagihan Sekarang
+ <%= t('admin.bc.stop','Hentikan') %>
+ <%= t('admin.bc.send','Kirim Tagihan Sekarang') %>
diff --git a/views/admin/mikrotik_display.ejs b/views/admin/mikrotik_display.ejs
index 614bdbc..8b2792f 100644
--- a/views/admin/mikrotik_display.ejs
+++ b/views/admin/mikrotik_display.ejs
@@ -827,7 +827,7 @@
-
Menghubungkan ke MikroTik...
+
<%= t('admin.md.connecting','Menghubungkan ke MikroTik...') %>
@@ -857,16 +857,16 @@
-
-
+
+
- Fullscreen
+ <%= t('admin.md.fullscreen','Layar Penuh') %>
- Keluar
+ <%= t('admin.md.exit','Keluar') %>
@@ -1079,22 +1079,22 @@
}, 1000);
}
- // Toggle HTML5 Fullscreen Mode
- function toggleFullscreen() {
+ // Toggle HTML5 <%= t('admin.md.fullscreen','Layar Penuh') %> Mode
+ function toggle<%= t('admin.md.fullscreen','Layar Penuh') %>() {
const icon = document.getElementById('fs-btn-icon');
const text = document.getElementById('fs-btn-text');
if (!document.fullscreenElement) {
- document.documentElement.requestFullscreen().then(() => {
+ document.documentElement.request<%= t('admin.md.fullscreen','Layar Penuh') %>().then(() => {
icon.className = 'bi bi-fullscreen-exit';
text.textContent = 'Exit Screen';
}).catch(err => {
console.error(`Error enabling fullscreen: ${err.message}`);
});
} else {
- document.exitFullscreen().then(() => {
+ document.exit<%= t('admin.md.fullscreen','Layar Penuh') %>().then(() => {
icon.className = 'bi bi-fullscreen';
- text.textContent = 'Fullscreen';
+ text.textContent = '<%= t('admin.md.fullscreen','Layar Penuh') %>';
});
}
}
diff --git a/views/admin/routers.ejs b/views/admin/routers.ejs
index e94227b..c58b5a1 100644
--- a/views/admin/routers.ejs
+++ b/views/admin/routers.ejs
@@ -335,15 +335,15 @@
-
Daftar Router MikroTik (<%= routers.length %>)
+ <%= t('admin.rt.list','Daftar Router MikroTik') %> (<%= routers.length %>)
- Grid Kartu
+ <%= t('admin.rt.view_grid','Grid Kartu') %>
Tabel
-
Script Isolir
-
Tambah Router
+
<%= t('admin.rt.script_isolir','Script Isolir') %>
+
<%= t('admin.rt.add','Tambah Router') %>
@@ -354,7 +354,7 @@
<%= t('admin.routers.empty', 'Belum ada router yang ditambahkan') %>
- Tambah Router Sekarang
+ <%= t('admin.rt.add_now','Tambah Router Sekarang') %>
<% } %>
@@ -409,7 +409,7 @@
-
-
PPPoE Aktif
+
<%= t('admin.rt.pppoe_active','PPPoE Aktif') %>
-
+
Detail Live
-
+
@@ -473,12 +473,12 @@
@@ -495,7 +495,7 @@
- Detail Live Router
+ <%= t('admin.rt.live_title','Detail Live Router') %>
×
@@ -511,7 +511,7 @@
-
+
@@ -836,7 +836,7 @@ async function openLiveDetailModal(id) {
Pelanggan DB ${l.customersCount}
-
PPPoE Aktif ${l.activePppoe}
+
<%= t('admin.rt.pppoe_active','PPPoE Aktif') %> ${l.activePppoe}
Hotspot Aktif ${l.activeHotspot}
diff --git a/views/admin/whatsapp_templates.ejs b/views/admin/whatsapp_templates.ejs
index 3274b9f..c246f5e 100644
--- a/views/admin/whatsapp_templates.ejs
+++ b/views/admin/whatsapp_templates.ejs
@@ -46,12 +46,12 @@
-
Pengaturan Template Notifikasi WhatsApp
+ <%= t('admin.wt.title','Pengaturan Template Notifikasi WhatsApp') %>
-
Template Pengingat Tagihan (Bulanan / Manual)
+
<%= t('admin.wt.lbl_reminder','Template Pengingat Tagihan (Bulanan / Manual)') %>
<%= templates.whatsapp_auto_billing_message %>
Variabel: {{id_pelanggan}} <%= t('common.required','(wajib)') %> , {{pppoe}} (Username PPPoE, opsional) , {{nama}} , {{tagihan}} , {{rincian}} , {{paket}} , {{link}} .
@@ -59,7 +59,7 @@
-
Template Tagihan QRIS (Manual)
+
<%= t('admin.wt.lbl_qris','Template Tagihan QRIS (Manual)') %>
<%= templates.whatsapp_billing_qris_message %>
Variabel:
{{id_pelanggan}} <%= t('common.required','(wajib)') %> ,
{{pppoe}} (Username PPPoE, opsional) ,
{{nama}} ,
{{periode}} ,
{{paket}} ,
{{qris_nominal}} ,
{{qris_kode}} ,
{{qris_qr}} .
@@ -69,7 +69,7 @@
-
Template Pembayaran Sukses (Lunas)
+
<%= t('admin.wt.lbl_success','Template Pembayaran Sukses (Lunas)') %>
<%= templates.whatsapp_payment_success_message %>
Variabel: {{id_pelanggan}} <%= t('common.required','(wajib)') %> , {{pppoe}} (Username PPPoE, opsional) , {{nama}} , {{no_invoice}} , {{paket}} , {{periode}} , {{waktu}} , {{metode}} , {{total}} , {{company}} , {{company_phone}} , {{link_portal}} , {{link_pdf}} (link PDF invoice).
@@ -77,7 +77,7 @@
-
Template Pemberitahuan Isolir (Otomatis / Manual)
+
<%= t('admin.wt.lbl_isolir','Template Pemberitahuan Isolir (Otomatis / Manual)') %>
<%= templates.whatsapp_isolir_message %>
Variabel: {{id_pelanggan}} <%= t('common.required','(wajib)') %> , {{pppoe}} (Username PPPoE, opsional) , {{nama}} , {{paket}} , {{tagihan}} , {{link}} .
@@ -85,19 +85,19 @@
-
Template Peringatan Tunggakan (menuju nonaktif)
+
<%= t('admin.wt.lbl_arrears','Template Peringatan Tunggakan (menuju nonaktif)') %>
-
Template Auto-Lunas (Rp 0 / auto_isolate=0)
+
<%= t('admin.wt.lbl_autopaid','Template Auto-Lunas (Rp 0 / auto_isolate=0)') %>
<%= templates.whatsapp_autolunas_message %>
Variabel: {{id_pelanggan}} , {{nama}} , {{tagihan}} , {{rincian}} , {{company}} .
-
Template Auto-Nonaktif (menunggak)
+
<%= t('admin.wt.lbl_autoinactive','Template Auto-Nonaktif (menunggak)') %>
<%= templates.whatsapp_deactivated_message %>
Variabel: {{id_pelanggan}} , {{nama}} , {{bulan_nunggak}} , {{batas}} , {{link}} , {{company}} .
-
Template Auto-Hapus (bulan ke-4)
+
<%= t('admin.wt.lbl_autodelete','Template Auto-Hapus (bulan ke-4)') %>
<%= templates.whatsapp_deleted_message %>
Variabel: {{id_pelanggan}} , {{nama}} , {{bulan_nunggak}} , {{company}} .
@@ -112,11 +112,11 @@
Muat Template Struk Resmi Lengkap
Muat Template Ringkas (Spintax Anti-Ban)
-
Konversi Format Meta Cloud API
+
<%= t('admin.wt.convert','Konversi Format Meta Cloud API') %>
Setiap template wajib menyertakan variabel ID Pelanggan {{id_pelanggan}} .
-
Simpan Template
+
<%= t('admin.wt.save','Simpan Template') %>
@@ -125,17 +125,17 @@