Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ FloCafe runs on real business data that must survive software upgrades.

## Translations and i18n

FloCafe currently provides translations for English (`en`), Spanish (`es`), French (`fr`), Brazilian Portuguese (`pt`), Russian (`ru`), Filipino (`fil`), Turkish (`tr`), Persian (`fa`, including RTL support), Arabic (`ar`, including RTL support), Urdu (`ur`, including RTL support), German (`de`), Italian (`it`), Japanese (`ja`), Simplified Chinese (`zh`), Traditional Chinese for Taiwan (`zh-tw`), Korean (`ko`), Bahasa Indonesia (`id`), Dutch (`nl`), Hindi (`hi`), Bengali/Bangla (`bn`), Albanian (`sq`), Vietnamese (`vi`), Thai (`th`), Nepali (`ne`), Hausa (`ha`), Javanese (`jv`), Marathi (`mr`), Swahili (`sw`), and Punjabi (`pa`).
FloCafe currently provides translations for English (`en`), Spanish (`es`), French (`fr`), Brazilian Portuguese (`pt`), Russian (`ru`), Filipino (`fil`), Turkish (`tr`), Persian (`fa`, including RTL support), Arabic (`ar`, including RTL support), Urdu (`ur`, including RTL support), German (`de`), Italian (`it`), Japanese (`ja`), Simplified Chinese (`zh`), Traditional Chinese for Taiwan (`zh-tw`), Korean (`ko`), Bahasa Indonesia (`id`), Dutch (`nl`), Hindi (`hi`), Bengali/Bangla (`bn`), Albanian (`sq`), Vietnamese (`vi`), Thai (`th`), Nepali (`ne`), Hausa (`ha`), Javanese (`jv`), Marathi (`mr`), Swahili (`sw`), Punjabi (`pa`), and Telugu (`te`).

- **Existing languages:** Narrowly scoped fixes and improvements to existing translation strings are always welcome. Verify changes with `npm run i18n:check`.
- **New languages:** Adding an entirely new language requires maintainer coordination through an issue first.
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ Both scripts ask whether to keep application data. Do not choose their data-purg

- **Offline-first architecture:** Local SQLite storage with auto-migrating schema and fail-closed safety.
- **Granular roles & permissions:** Owner, Manager, Cashier, Server, and Chef roles with context-aware access policies.
- **Multi-language support:** 29 localized languages with full bidirectional RTL rendering (Arabic, Persian, Urdu).
- **Multi-language support:** 30 localized languages with full bidirectional RTL rendering (Arabic, Persian, Urdu).
- **131 Country profiles:** 109 world currencies with localized phone parsing, number formatting, and timezone handling.
- **Country tax packs:** Signed regional tax pack integration and manual rule engine.
- **Automated database backups:** Pre-migration automatic snapshots, manual exports, and optional Google Drive sync.
Expand Down Expand Up @@ -248,6 +248,7 @@ FloCafe includes UI translations for:
- Marathi
- Swahili
- Punjabi
- Telugu

UI language is independent of store country and regional settings, and tax calculation rules remain a separate concern. For details on contributing translations or adding languages, see the [Internationalization and translation guide](docs/architecture/internationalization.md).

Expand Down
6 changes: 3 additions & 3 deletions docs/architecture/frontend.md
Original file line number Diff line number Diff line change
Expand Up @@ -542,10 +542,10 @@ and the auth store keeps the languages that failed. Every print action calls
a receipt warning saying English labels were used. A receipt that renders in the wrong language is
a financial document, not a cosmetic bug.

## Internationalization: 29 locales
## Internationalization: 30 locales

[`frontend/src/lib/i18n/languages.ts`](../../frontend/src/lib/i18n/languages.ts) is the renderer's
registry. It holds 29 entries, and
registry. It holds 30 entries, and
[`frontend/src/lib/i18n/messages/`](../../frontend/src/lib/i18n/messages) holds one JSON file per
entry. Each registry entry carries the BCP-47 locale tag, the native name, the text direction, the
`selectable` flag, and a dynamic chunk loader.
Expand Down Expand Up @@ -667,7 +667,7 @@ frontend/src/
│ └── updates/ Update download prompt and install guard dialog
├── hooks/ Printer, KDS, theme, update, cash, approver, and formatting hooks
├── lib/
│ ├── i18n/ Language registry, loader, and the 29 message files
│ ├── i18n/ Language registry, loader, and the 30 message files
│ ├── printer/ Encoders, document builders, warnings, WebUSB bridge,
│ │ and the delivery-slip encoder and browser renderer
│ ├── updates/ Beta channel, auto-check, and restart-and-install helpers
Expand Down
6 changes: 3 additions & 3 deletions docs/architecture/internationalization.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Internationalization

FloCafe supports 29 UI languages. This page describes how a language is declared, loaded, and
FloCafe supports 30 UI languages. This page describes how a language is declared, loaded, and
validated, and how the UI language stays separate from a store's regional settings.

## Source of truth and the parity rule
Expand All @@ -15,8 +15,8 @@ translation test fails. `npm run i18n:check` runs `tests/translations.test.ts`,
registry and file consistency, key parity, ICU validity, placeholder and tag parity, per-language
completeness, and frontend key safety.

The 29 committed catalogues are: `ar`, `bn`, `de`, `en`, `es`, `fa`, `fil`, `fr`, `ha`, `hi`, `id`,
`it`, `ja`, `jv`, `ko`, `mr`, `ne`, `nl`, `pa`, `pt`, `ru`, `sq`, `sw`, `th`, `tr`, `ur`, `vi`, `zh`, `zh-tw`.
The 30 committed catalogues are: `ar`, `bn`, `de`, `en`, `es`, `fa`, `fil`, `fr`, `ha`, `hi`, `id`,
`it`, `ja`, `jv`, `ko`, `mr`, `ne`, `nl`, `pa`, `pt`, `ru`, `sq`, `sw`, `te`, `th`, `tr`, `ur`, `vi`, `zh`, `zh-tw`.

## The registry

Expand Down
2 changes: 1 addition & 1 deletion docs/architecture/printing.md
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ The drawer pulse is the ESC/POS sequence `ESC p 0 0x19 0xFA`. It is emitted only

`shared/print/width.ts` measures in grapheme clusters and display cells, not UTF-16 code units. A
combining mark attaches to its base, an Indic conjunct is never split at a virama
(U+094D, U+09CD, U+0A4D), and a ZWNJ stays with the grapheme it follows. Measuring in code units silently
(U+094D, U+09CD, U+0A4D, U+0C4D), and a ZWNJ stays with the grapheme it follows. Measuring in code units silently
breaks every non-BMP and every combining-script receipt, which is why every renderer in the
pipeline uses this helper rather than `String.length`.

Expand Down
20 changes: 10 additions & 10 deletions docs/guides/adding-a-language.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ While translating:
such as an email or IP address. Anything else renders as untranslated English to your users, and
the check will reject it.
- For a language with combining marks, the value must be in Unicode NFC. `tests/translations.test.ts`
enforces NFC for the languages it checks by name, which are `ha`, `jv`, `mr`, `ne`, `pa`, `ru`, `sw`, `th`, and `vi`.
- Avoid invisible formatting characters. The Russian, Javanese, and Marathi validators in
enforces NFC for the languages it checks by name, which are `ha`, `jv`, `mr`, `ne`, `pa`, `ru`, `sw`, `te`, `th`, and `vi`.
- Avoid invisible formatting characters. The Russian, Javanese, Marathi, and Telugu validators in
`tests/translations.test.ts` reject U+200B, U+200C, U+200D, U+00AD, and U+FEFF, because they
corrupt printed text silently. Other language validators do not check for them yet, so this is
your responsibility to get right rather than something the test will catch.
Expand All @@ -74,7 +74,7 @@ While translating:
## 4. Register the intentional-identical keys

Each language has a `<LANG>_INTENTIONAL_IDENTICAL` set in `tests/translations.test.ts` listing the
keys allowed to be English-identical, most with a comment saying why. There are 24 such sets.
keys allowed to be English-identical, most with a comment saying why. There are 25 such sets.

Add an entry for every key you deliberately left identical, with a comment giving the reason. Without
the entry the check fails, and the failure is what keeps an accidentally untranslated string from
Expand Down Expand Up @@ -129,13 +129,13 @@ language is not covered by them until you add it:

| File | Array | Current coverage |
| --- | --- | --- |
| `tests/phase3-print-regressions.test.ts` | `languages` | 28 of 29; omits `ar`. |
| `tests/issue-241-localized-errors.test.ts` | `LANGUAGES` | All 29 (`Object.keys(LANGUAGE_REGISTRY)`). |
| `tests/rtl-setup-auth-settings.test.ts` | `languages` | 25 of 29; omits `ar`, `de`, `fil`, `tr`. |
| `tests/browser-receipts.test.ts` | loop array | 25 of 29; omits `ar`, `de`, `fil`, `tr`. |
| `tests/i18n-audit-remediations.test.ts` | two loop arrays | 25 of 29 each; omits `ar`, `de`, `fil`, `tr`. |
| `tests/rtl-kds-server-whatsapp.test.ts` | four loop arrays | 25 of 29 for the all-language loops, 23 for the LTR-only loop. |
| `tests/rtl-dashboard-pos-common.test.ts` | two LTR-only loop arrays | 23 of 29; omits the RTL languages plus `de`, `fil`, `tr`. |
| `tests/phase3-print-regressions.test.ts` | `languages` | 29 of 30; omits `ar`. |
| `tests/issue-241-localized-errors.test.ts` | `LANGUAGES` | All 30 (`Object.keys(LANGUAGE_REGISTRY)`). |
| `tests/rtl-setup-auth-settings.test.ts` | `languages` | 26 of 30; omits `ar`, `de`, `fil`, `tr`. |
| `tests/browser-receipts.test.ts` | loop array | 26 of 30; omits `ar`, `de`, `fil`, `tr`. |
| `tests/i18n-audit-remediations.test.ts` | two loop arrays | 26 of 30 each; omits `ar`, `de`, `fil`, `tr`. |
| `tests/rtl-kds-server-whatsapp.test.ts` | four loop arrays | 26 of 30 for the all-language loops, 24 for the LTR-only loop. |
| `tests/rtl-dashboard-pos-common.test.ts` | two LTR-only loop arrays | 24 of 30; omits the RTL languages plus `de`, `fil`, `tr`. |

This is a known baseline, not something this procedure introduces. Several of these arrays already
omit registered locales, so they are not a reliable coverage signal. The arrays whose names include
Expand Down
12 changes: 9 additions & 3 deletions docs/reference/translation-terminology.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ Pinned by `tests/print-kernel.test.ts`.

## Indic conjuncts are never split

A Devanagari, Bengali, or Gurmukhi conjunct is a single grapheme cluster and must stay one. The
virama characters that join them, U+094D in Devanagari, U+09CD in Bengali, and U+0A4D in Gurmukhi,
A Devanagari, Bengali, Gurmukhi, or Telugu conjunct is a single grapheme cluster and must stay one. The
virama characters that join them, U+094D in Devanagari, U+09CD in Bengali, U+0A4D in Gurmukhi, and U+0C4D in Telugu,
are the ones a naive splitter would break.

Pinned by `tests/print-kernel.test.ts`, which asserts that wrapping a two-cluster Indic string at one
Expand All @@ -55,7 +55,7 @@ is invisible on screen and corrupts comparison, searching, and print labels.
identically and wrap identically after normalisation, which is what makes NFC a requirement rather
than a preference. `tests/translations.test.ts` rejects a `vi.json` value that is not already NFC.

The same NFC requirement is enforced for `ha.json`, `jv.json`, `mr.json`, `ne.json`, `pa.json`, `ru.json`, `sw.json`, and `th.json`.
The same NFC requirement is enforced for `ha.json`, `jv.json`, `mr.json`, `ne.json`, `pa.json`, `ru.json`, `sw.json`, `te.json`, and `th.json`.

## Albanian and Vietnamese on a generic thermal printer

Expand Down Expand Up @@ -198,6 +198,12 @@ pay-out on both the dashboard and the Z-report, and takeaway is `ਟੇਕਅਵ
`pos.*`. Gurmukhi virama conjuncts (`ਪ੍ਰ`, `ਸ੍ਰ`) and combining signs (`ੰ` tippi, `ਂ` bindi, `ੱ`
addak, `਼` nukta) must remain NFC-normalized and attached to their base consonant.

**Telugu.** Subtotal `ఉప మొత్తం` and grand total `మొత్తం` are distinct labels and must stay
distinct on the receipt. The vegetarian tags are `శాకాహారం` and the non-vegetarian tags
`మాంసాహారం`, identical between `pos.*` and `products.*`. Cash movement uses `నగదు జమ` for pay-in
and `నగదు ఉపసంహరణ` for pay-out, identically on the dashboard and on the Z-report. Takeaway is
`టేక్అవే` and delivery is `డెలివరీ`.

## Verification

The contracts on this page are pinned by `tests/print-kernel.test.ts`,
Expand Down
1 change: 1 addition & 0 deletions frontend/e2e/phase7-setup-print-i18n.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ const LABELS: Record<string, { basic: string; web: string; whatsapp: string }> =
web: 'ਵੈੱਬ ਪ੍ਰਿੰਟ (ਬ੍ਰਾਊਜ਼ਰ)',
whatsapp: 'WhatsApp ਸ਼ੇਅਰ',
},
te: { basic: 'ప్రాథమిక రసీదు (థర్మల్)', web: 'వెబ్ ప్రింట్ (బ్రౌజర్)', whatsapp: 'WhatsApp షేర్' },
};

async function loginAsOwner(page: Page): Promise<void> {
Expand Down
Loading
Loading