feat(02-03): strings module + text-only command surface with Malay aliases (D-01..D-05)
- src/i18n/strings.ts: single copy source of truth, en + ms identical key-sets, clinical/honest start + privacy copy, pickLocale fallback rule - /start /mula, /help, /privacy /privasi handlers: plain text replies with link previews disabled, zero menu/button surface (D-02, grep-gated) - createBot(env) factory registers exactly the phase command stack - scripts/register-commands.mjs: one-time setMyCommands, two scopes (default + language_code ms), descriptions derived from helpMenu — no inline copy, no future-command stubs (D-03/D-05) - test/commands.test.mjs: CS1-CS6 gates — locale parity, pickLocale matrix, PRIV-01 copy gate vs never-facts + TTL wording, D-02 structural grep, D-03 menu gap-freedom, D-04 alias completeness (27 passing)
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* strings.ts — the copy's tokens.css (D-05).
|
||||
* -------------------------------------------
|
||||
* Every user-facing string the bot sends lives here and NOWHERE else —
|
||||
* handlers import from this module, the setMyCommands registration script
|
||||
* derives its menu descriptions from `helpMenu`, and there is no inline
|
||||
* copy anywhere in the command tree.
|
||||
*
|
||||
* Locales: English default ("en") + Bahasa Melayu ("ms"). Tone is clinical
|
||||
* + honest (D-01): one sentence for what araharah is, plain statements of
|
||||
* what is stored and what is not — no emoji, no marketing flourishes.
|
||||
*
|
||||
* Claim-surface honesty (D-17 / PRIV-01): `privacyStored` names the exact
|
||||
* D-08 claim fields written by src/kv/sessions.ts (user id, chat id,
|
||||
* language, ~15-minute TTL) and nothing more; `privacyNotStored` names the
|
||||
* exact never-facts. Any matching change to the storage layer must be
|
||||
* reflected here and the copy gate in test/commands.test.mjs will keep
|
||||
* the two in step.
|
||||
*/
|
||||
|
||||
export interface BotStrings {
|
||||
/** D-01: what araharah is, in one sentence. */
|
||||
startIntro: string;
|
||||
/** PRIV-01: exactly what is stored — must mirror the D-08 claim fields and the TTL. */
|
||||
privacyStored: string;
|
||||
/** PRIV-01: exactly what is NOT stored — the never-facts. */
|
||||
privacyNotStored: string;
|
||||
/** D-03: lists only commands that exist in this phase — derived lines
|
||||
* ("cmd — description") feed the setMyCommands registration script. */
|
||||
helpMenu: string;
|
||||
}
|
||||
|
||||
export type Locale = "en" | "ms";
|
||||
|
||||
export const strings: Record<Locale, BotStrings> = {
|
||||
en: {
|
||||
startIntro:
|
||||
"araharah — navigation through Telegram for the KL metro. Everything it knows about you expires in about fifteen minutes; /privacy shows exactly what that means.",
|
||||
privacyStored:
|
||||
"What araharah keeps: your Telegram user id, this chat id, and your interface language — kept only for about 15 minutes so the bot can answer you, then gone.",
|
||||
privacyNotStored:
|
||||
"What araharah never keeps: your location, your routes, your trip history, any profile about you.",
|
||||
helpMenu:
|
||||
"Commands:\n/help — How araharah works and what it keeps\n/privacy — What araharah stores and never stores",
|
||||
},
|
||||
ms: {
|
||||
startIntro:
|
||||
"araharah — navigasi melalui Telegram untuk kawasan KL. Semua yang diketahui tentang anda luput dalam lebih kurang lima belas minit; /privacy menunjukkan dengan tepat maksudnya.",
|
||||
privacyStored:
|
||||
"Yang araharah simpan: id pengguna Telegram anda, id chat ini, dan bahasa antara muka anda — disimpan hanya sekitar 15 minit supaya bot boleh menjawab anda, kemudian hilang.",
|
||||
privacyNotStored:
|
||||
"Yang araharah tidak pernah simpan: lokasi anda, laluan anda, sejarah perjalanan anda, apa-apa profil tentang anda.",
|
||||
helpMenu:
|
||||
"Arahan:\n/help — Cara araharah berfungsi dan apa yang ia simpan\n/privacy — Apa yang araharah simpan dan tidak pernah simpan",
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* D-05 fallback rule: a Telegram `language_code` that starts with the
|
||||
* Malay tag selects "ms"; every other value (including null/undefined and
|
||||
* unknown languages) falls back to English.
|
||||
*/
|
||||
export function pickLocale(telegramLanguageCode?: string | null): Locale {
|
||||
const code = telegramLanguageCode ?? "";
|
||||
return code === "ms" || code.startsWith("ms-") ? "ms" : "en";
|
||||
}
|
||||
Reference in New Issue
Block a user