Languages and right-to-left layout¶
KhataOS shows its interface in English or Arabic. Arabic mirrors the window right to left. Keyboard shortcuts are the same in both languages.
Code: apps/desktop/src/i18n.ts (lookup, direction, saved choice),
apps/desktop/src/locales/ar.ts (the Arabic table),
apps/desktop/src/locales/ar-errors.ts (Arabic for messages from Rust), the
Arabic block at the end of apps/desktop/src/style.css,
apps/desktop/scripts/strings.mjs (the review export), and the *_ar print
functions in crates/khata-print.
Languages¶
| Code | Name | Direction | Status |
|---|---|---|---|
en |
English | left to right | source language, complete |
ar |
العربية (Arabic, Gulf accounting usage) | right to left | every screen's own text (stages 1–3); see "Still English" below |
Switching¶
- Ctrl+K opens the Language list anywhere, with or without a company
open. It is TallyPrime's language key and is bound in
crates/khata-keymap(DisplayLanguage). - The company list has a Language: English / العربية row.
- Company (Alt+K) → Language.
- The choice is kept on this computer, and also for the signed-in user of that company, so two people sharing a PC each get their own. It is a preference, not company data, so it never syncs.
- English is the default. A company whose country is Saudi Arabia (
SA) or the UAE (AE) gets a one-time hint, in both languages, that Arabic is available. Nothing switches by itself. The hint doesn't appear for anyone who has already chosen a language. - The window switches at once: the bars, the menu, and the home screen, company list or sign-in screen. Other screens that are already open stay in their language until they are opened again, so unsaved entries are never lost.
Keyboard¶
Shortcuts depend on the physical key, so an Arabic keyboard layout uses the same keys as an English one.
- Chords (
Ctrl+A,Alt+F7,F8…) are built inkeys.ts. With Ctrl or Alt held, or when the typed character isn't a Latin letter or digit, the key comes fromKeyboardEvent.code. On an Arabic layout the C key types "ؤ" but still sendsC, so the home menu's C opens Create. Latin layouts such as AZERTY and Dvorak keep their own letters. - Single-letter answers (Y/N in prompts, D/C on a Dr/Cr field, Yes/No
fields, choice dialogs) use
letterOf(e)fromkeys.ts, which follows the same rule. - In Arabic, the home menu shows each hotkey as a small key cap before the label, because the label has no Latin letter to underline. Yes/No prompts show Y and N the same way.
Decisions¶
- Digits: Latin digits (0–9) in every language, as most Gulf accounting
software and Saudi and UAE financial statements use them. Arabic-Indic
digits (٠–٩), Persian digits and the Arabic decimal and thousands
separators are accepted when typed, in amounts (
money.parse) and dates (dates.parseUser), and are stored as Latin digits. - Grouping: unchanged. It follows the company's country
(
useGroupingOf): 2,50,000 for India, 250,000 for everyone else. - Amount alignment: amounts stay right-aligned in their column in
Arabic too, so decimal points line up. Each amount is a left-to-right
island (
direction: ltr), so a minus sign stays in front ("-1,200.00", not "1,200.00-"). The column order still mirrors: in Arabic, amount columns sit on the left. - Dr / Cr: the word is translated (مدين / دائن), in balances and on the voucher's Dr/Cr field. D and C still set that field.
- Dates: the same day-month-year order and Gregorian calendar, with Arabic month and day names: "19-أكتوبر-2026", "الاثنين". Typed dates accept everything they accepted before, plus Arabic month names, with or without hamza ("19 أكتوبر", "19 اكتوبر"). Hijri dates can be shown beside them (see "Hijri dates" below); the books stay Gregorian.
- Identifiers: a voucher number inside Arabic text is wrapped in a
left-to-right isolate (
ltr()ini18n.ts), so "26-27/4" isn't reordered. File paths are shown left to right. - Type: Arabic text is at least 14 px (body 15 px). Letter-spacing is off, because it pulls joined letters apart, and italics are off, because Arabic has no italic and browsers would slant it artificially. Font stack: Inter / the system font for Latin, then "Noto Naskh Arabic", "Segoe UI", "Geeza Pro", "Tahoma", system-ui for Arabic.
- CSV export of a report uses its column titles as shown, so an Arabic window exports Arabic titles. Figures stay Latin.
Right-to-left rules for CSS¶
<html dir="rtl" lang="ar"> is set by setLanguage. Most of the layout
mirrors by itself. Keep it that way:
- Use logical properties:
margin-inline-start,padding-inline,border-inline-start,inset-inline-end,text-align: start | end. Never useleft,right,margin-leftortext-align: leftfor something that should follow the reading direction. - Grid and flex rows mirror by themselves: the F-key panel moves to the left, form labels sit to the right of their fields, and report columns run right to left.
- Physical values that must flip go through tokens on
:root, redefined under[dir="rtl"].--edgeis the "current row" stripe drawn as an inset shadow, and--spineis the direction of the brand mark's accent. - Amounts: class
numorfield amount. Nevertext-align: left. - Small type: write
font-size: max(var(--text-min), 12px)for anything under 14 px, andletter-spacing: calc(0.4px * var(--tracking))for caps labels. Usefont-style: var(--italic)instead ofitalic. - Arrows in text that point "forward" (→) become ← in Arabic translations ("Company ← Language"). ↑ ↓ and ▲ ▼ don't change.
Adding a string¶
- Write the English text in the code, wrapped in
t():t("Pick the party ledger."). Variable parts are placeholders:t("{kind} No. {number} saved.", { kind: kindName(k), number: ltr(n) }). Never build sentences by joining fragments: word order differs between languages. - Add the same English text, character for character, to
src/locales/ar.ts, one entry per line:"Pick the party ledger.": "اختر حساب الطرف.",. Keep every{name}. Mark a term you're unsure of with// ar: check. - A missing entry is never an error: the English shows instead.
- Never pass user data through
t(). A ledger named "Sales" would otherwise turn into "فاتورة مبيعات". Labels from the books (ledger, group and item names, backend messages) are shown as they are. - A count changes the words around it in Arabic (zero, one, two, few,
many, other). Write the Arabic entry as an ICU plural block, all on one
line, with
#for the number:"Added {n} entries.": "{n, plural, zero {لم يُضف أي قيد.} one {أُضيف قيد واحد.} two {أُضيف قيدان.} few {أُضيفت # قيود.} many {أُضيف # قيدًا.} other {أُضيف # قيد.}}",.t()picks the branch withIntl.PluralRules("ar")(=0,=1… first, then the category, thenother). In new code, prefertn(n, { one: "Added {n} entry.", other: "Added {n} entries." }): English picks one/other, and the Arabic is looked up under theothertext. Older call sites that choose between "{n} entry" and "{n} entries" themselves work too: the plural key's entry carries the block. - Run
pnpm strings --missing(fromapps/desktop) to list literalt("…")texts that have no translation. - A word whose Arabic depends on where it's used gets its use in
brackets as a second key, looked up with
inUse():inUse("Particulars", t("Particulars (figure)"))is البند in a stock or statement table, while plain "Particulars" (اسم الحساب) heads the account column of vouchers and ledgers. English always shows the plain word. Also "Sales (figure)", "Section (return table)".
The shell translates its own fixed text: screen titles, bottom-bar and
F-key labels, prompts, dialogs and messages pass through t() when they are
shown. A screen can pass English there, and the translation is used when
the table has one. The same applies to picker titles and column names, and to
the rows of MenuScreen menus. Menu rows keep their English label as an
alias, so typing "trial" still finds ميزان المراجعة.
Translator review¶
cd apps/desktop
pnpm strings > ar.csv # english, translation, status (ok / check), note
pnpm strings --missing # literal t("…") texts with no translation yet
Open ar.csv in a spreadsheet. Review the rows marked check first: India-only
reports (GSTR, Schedule III, MSME, TDS), Contra (سند تحويل), Stock Journal,
Party, Batch and the relay terms. Send corrections as edits to
src/locales/ar.ts.
Stage 1 (done)¶
Shell chrome: top bar, title strip, bottom bar, F-key panel, prompts, Yes/No and field dialogs, and the shell's messages. Also: the home menu and its company summary; the Create, Alter (with its record lists), Company, Import, Go To and Language menus; the Report Directory and Find; the company list and the create/open company forms; the sign-in screen; and the voucher screen's fixed labels (voucher types, field and column labels, totals, entry modes, pickers, the common prompts and checks). Shared report chrome is also translated: report titles, "As at", periods, column titles, totals, empty messages and the date and period dialogs.
Stage 2 (done)¶
The ledger form (with its TDS fields), the stock item, stock group and unit forms, company details, the tax rate list and form, voucher type numbering and the ZATCA seller details, the VAT 201, KSA VAT, UK VAT and GST F5 returns, bank reconciliation, the statement review and the cheque register, the users screen and the user form, the ZATCA and e-invoice export choices, and the voucher screen's TDS messages.
Stage 3 (done)¶
- Masters: the group form (
group.ts), godowns (godown.ts), the country pick (country.ts) and the country names everywhere they are listed (the ledger form's Country list too). Country names are pack labels, not user data, so they go throught(); the English name stays a search alias, so "germ" still finds ألمانيا. - Entry: the stock journal (
stockjournal.ts), bill-wise details (bills.ts; New Ref / Agst Ref / Advance / On Account are translated on screen and stay English in what is saved), and the voucher screen's remaining messages (stock below zero, quantities, unit decimals, sales and purchase ledgers, tax-setup hints, saved-version notes). - Choices:
choose(sharing.ts) and the e-invoice export choice translate their labels. The answer is still the English label's letter, shown as a key cap before the Arabic label (hotkeyLabelindom.tsalways uses the cap for an Arabic label, even when a Latin word in it, such as "e-Way", contains the letter). - Reports: ledger statement and group summary titles, Chart of
Accounts (indented from the right), outstanding, ageing and MSME, the
Day Book cancel prompts, P&L stock and gross lines, the Tally import and
rebase reports, the stock summary, stock item vouchers, godown and batch
summaries. Voucher numbers in table cells are
<bdi>, so they keep their order without adding characters to the CSV. - Dashboard and Ratio Analysis (
analysis.ts): tiles, chart titles, legends, axes, tooltips, tables, the chart/table toggle (Ctrl+H) and the CSV. In Arabic the charts mirror: the value axis is on the right, months run right to left, legends and grouped bars put Sales first on the right, horizontal bars grow to the left with names on the right. Axis figures use words (1.2 ألف, 3 مليون; لاك / كرور for India), kept left to right. Chart text is at least 14 px. Names from the books keep their own direction inside the chart (a first-strong isolate), so a cut name ends in "…" on its own side. - Tax (India): GSTR-1 and GSTR-3B (
gst.ts), Schedule III (schedule3.ts, every line, heading and total label fromkhata_in::schedule3), the e-invoice and e-way bill detail forms and the problems list (einvoice.ts). Official form and table names stay Latin in a left-to-right isolate with an Arabic gloss: "GSTR-1 (التوريدات الصادرة)", "فواتير B2B بين المنشآت (4A, 4B)". IGST, CGST, SGST/UTGST, HSN/SAC, UQC, GSTIN and IRN stay as written. India-only terms are markedar: check. - Sharing and security: Exchange, Client/Server configuration, Online
Access, Connectivity Status, Remote Access and invites, Connect to
company, sync settings, Send to accountant, and the company menu's
screens (
sharing.ts,company.ts). URLs, addresses, codes, tokens and paths are inltr(). - Shell: every action label in the keymap has an entry, and a toast whose text has no translation (a message from Rust) is shown left to right, so its punctuation stays in place.
Printing in Arabic¶
Reports and vouchers print in the window's language. The frontend passes it
with every print: api.printReport(spec, language()) puts "language": "ar"
beside the report's fields (PrintSpec in src-tauri/src/export.rs, which
flattens ReportSpec), and api.printVoucher(id, language(), hijri) passes
it to print_voucher. Anything other than ar (or ar-…), and a missing
language, prints English, as before. CSV export has no language: it takes
the column titles as shown.
Arabic function (khata-print) |
|
|---|---|
| Trial Balance | trial_balance_html_ar |
| Day Book | day_book_html_ar |
| Ledger statement | ledger_statement_html_ar |
| Profit & Loss A/c | profit_and_loss_html_ar (expenses on the right) |
| Balance Sheet | balance_sheet_html_ar (liabilities on the right) |
Any other report ({report: "table"}) |
table_html_ar, with the screen's own title, columns and cells |
| Payment, Receipt, Contra, Journal, Purchase, Stock Journal; Sales and notes outside KSA and the UAE | invoice_html_ar |
| KSA and UAE invoices, credit and debit notes | unchanged: bilingual (invoice_html_bilingual_hijri) |
- The document is
<html dir="rtl" lang="ar">, and the body is wrapped in<main class="ar" dir="rtl" lang="ar">, because the print window copies only the body's children (main.ts printMode). - Labels reuse the app's wording (
ar.ts): ميزان المراجعة, دفتر اليومية, اسم الحساب, مدين / دائن, الرصيد الافتتاحي, صافي الربح… Voucher types are the app's names (سند صرف, فاتورة مبيعات…). Dates are "1-أبريل-2026". - Latin digits and the company's grouping. Amount cells are left-to-right
islands (
direction: ltr; unicode-bidi: isolate), right-aligned like the screen, so a minus stays in front. - Names from the books, voucher numbers, tax ids and the amount in words
are
<bdi>, so an English ledger name keeps its direction. The amount in words is Arabic tafqit ("فقط … لا غير"); see "Amounts in words". - Font stack: "Noto Naskh Arabic", "Segoe UI", "Geeza Pro", "Tahoma", system-ui; 11 pt, no letter-spacing, no italics.
- Schedule III statements and the tax return prints stay English. Cheques are laid out in English, with the amount in words in the bank country's wording ("Amounts in words").
Amounts in words¶
Invoices, notes, vouchers and cheques write the total in words the way the
company's country does. Code: crates/khata-core/src/words.rs
(amount_words(amount, currency, style) and WordsStyle);
khata_core::format::amount_in_words is unchanged for its existing callers
(the Indian RCM self-invoice). The desktop picks the style in
invoice::words and invoice_design::words_for_layout.
Which style prints
- English prints follow the company's country (
WordsStyle::for_country). English is the only non-Arabic print language today, so a German, French, Spanish, Mexican or Dutch company gets its own language's words on an otherwise English invoice.WordsStyle::for_print(country, "de")and the like are ready for when prints get those languages. - Arabic prints (
InvoiceLayout::Arabic) put Arabic inInvoiceDoc.total_in_words. - Bilingual KSA/UAE invoices and notes print the English, then the Arabic
from
InvoiceDoc.total_in_words_ar, right to left, in the HTML and the PDF. - A foreign-currency invoice uses the invoice currency's names in the company's style ("US Dollars … Only" from India).
- Cheques use the bank's country: the bank ledger's mailing country when it has one, else the company's. A foreign-currency bank account's cheque is in that currency (amount and words).
- KWD, BHD and OMR count thousandths (fils, baisa); JPY has no minor unit.
| Style | Countries | 4,500.25 |
|---|---|---|
| India | IN | Rupees Four Thousand Five Hundred and Twenty-Five Paise Only (lakh, crore; unchanged) |
| UK | GB | Four thousand five hundred pounds and twenty-five pence only |
| US | US | Four Thousand Five Hundred and 25/100 Dollars |
| Singapore | SG | Singapore Dollars Four Thousand Five Hundred and Cents Twenty-Five Only |
| International English | AE, SA and the rest | Saudi Riyals Four Thousand Five Hundred and Twenty-Five Halalas Only |
| Arabic | Arabic prints, bilingual KSA/UAE | فقط أربعة آلاف وخمسمائة ريال سعودي وخمس وعشرون هللةً لا غير |
| German | DE, AT | Viertausendfünfhundert Euro und fünfundzwanzig Cent |
| French | FR | Quatre mille cinq cents euros et vingt-cinq centimes |
| Spanish | ES | Cuatro mil quinientos euros con veinticinco céntimos |
| Mexico | MX | Cuatro mil quinientos pesos 25/100 M.N. |
| Dutch | NL | Vierduizend vijfhonderd euro en vijfentwintig cent |
Conventions and sources
- UK: words for pounds and pence, written out in full, British "and" after hundreds and before a last part under 100 ("one thousand and one"), then "only" so nothing can be added. Western Union UK, "How to write a cheque" (westernunion.com/blog/en/gb/how-to-write-a-cheque), whose examples "one thousand two hundred and forty-seven pounds and fifty pence only" and "fifty-six pounds and twenty-three pence only" are tests.
- US: the check line: dollars in words, cents as a fraction over 100, "00/100" for whole dollars, no "and" inside the dollars ("and" means the decimal point). Macalester College, "How to write a check" (PDF, macalester.edu/isp); Omni Calculator, "Dollars in words". The word "Dollars" is printed on the check form; we print it so the invoice line reads on its own.
- Singapore: currency first, cents after "and", "Only" at the end, as local cheque guides show (SingSaver, MoneySmart, Osome "How to write a cheque in Singapore"). Some write "and 50/100" instead; we use words.
- International English (Gulf and others): the Indian layout with international scales and the currency's full name ("UAE Dirhams", "Saudi Riyals"), as used on English Gulf invoices.
- Arabic (تفقيط): "فقط … لا غير" around the amount. The counted noun is
singular with واحد after it for 1 and dual for 2 ("ريالان سعوديان"),
plural after 3–10 with the number in the opposite gender ("ثلاثة ريالات",
"ثلاث هللات"), accusative singular after 11–99 ("أحد عشر ريالاً"),
genitive singular after hundreds and thousands ("مائة ريال", "ألف ريال").
مائتا / ألفا / مليونا (no ن) straight before a noun. The nationality
adjective agrees with the noun (سعودي / سعوديان / سعودية / سعودياً).
Feminine units (هللة، بيسة، روبية) use إحدى عشرة، اثنتا عشرة، خمس وعشرون.
The minor unit carries the adjective only when there are no major units
("فقط اثنتان وستون هللةً سعوديةً لا غير"). Spelling: مائة (Gulf usage),
hamzas written (أربعة، ألف، آلاف، إحدى). Sources: the published outputs
of the
tafqitDart package (pub.dev/packages/tafqit), e.g. "فقط ريالان سعوديان واثنتان وستون هللةً لاغير" and "فقط سالب ثلاثمائة وسبعة وخمسون ريالاً سعودياً وتسع وعشرون هللةً لاغير"; Mohsen Alyafei, "Get it grammatically correct, numbers to Arabic words" (dev.to) and nArabicWords ("ألفا جنيه", "واحد وعشرون طالبًا"); khaleejcalculators.com, تفقيط (the 3–10 / 11–99 / hundreds rule); موسوعة اللغة العربية, dorar.net/arabia/597 (1 and 2 use the singular and dual noun). All are tests inwords.rs. Currencies: SAR ريال / هللة, AED درهم / فلس, QAR ريال / درهم, OMR ريال / بيسة, KWD and BHD دينار / فلس, JOD دينار / فلس, EGP جنيه / قرش, USD دولار / سنت, GBP جنيه إسترليني / بنس, EUR يورو / سنت, INR روبية / بيسة, SGD. - German: written together below a million, "eine Million" and "Millionen" apart; "ein Euro"; "einhundert" and "eintausend" as amounts are written on cheques and transfers. Duden, "hundert" (duden.de, "eine Million dreihunderttausend"); the Duden rule that number words under a million are written together, as summarised by Studyflix and Klett.
- French: traditional spelling (hyphens below 100, "et" in 21–71), "vingt" and "cent" take an s when multiplied and ending the number (quatre-vingts, deux cents) and keep it before the nouns million and milliard (deux cents millions) but not before mille (quatre-vingt mille, deux cent mille); mille is invariable; "un million d'euros". Centimes rather than "cents" for the euro's hundredths, as cheques in France say. Sources: Académie française rules as summarised in school guides ("Écrire en lettres les adjectifs numéraux", lewebpedagogique.com; QuillBot, "Adjectif numéral").
- Spanish: "un" / "veintiún" before a masculine noun and before mil, "cien" alone, "ciento uno…", "un millón de euros", long scale ("mil millones"); "con … céntimos". RAE, Diccionario panhispánico de dudas, "veintiuno" and "uno" (apócope).
- Mexico: "importe con letra" on CFDI printouts: the pesos in words, then the centavos as "NN/100" and "M.N." (moneda nacional); a foreign currency gets its code ("… dólares 50/100 USD"). Printed CFDIs, e.g. "Cien Pesos 00/100 M.N.", "TRESCIENTOS DIECISIETE PESOS 00/100 MXN"; IFS Community, "Mexico localization … converting numbers to text" (the amount in words is a PDF feature, not in the XML). We print sentence case; many PACs print capitals.
- Dutch: written together up to duizend, a space after duizend and around miljoen/miljard ("vierduizend vijfhonderd", "twee miljoen"); ë in tweeëntwintig, drieëntwintig; "één euro" for one. Taaladvies (vlaanderen.be/taaladvies, "Getallen in cijfers of letters") and the Woordenlijst rule as quoted by webwoordenboek.nl ("na duizend komt een spatie").
- Hindi number words are not written (optional; Indian prints stay English).
For native review (marked here until a native speaker signs off):
- Arabic, all of it, especially: numbers ending in 1 or 2 after a larger part — we write "مائة وواحد ريال"، "ألف واثنان ريال" (common in software; some prefer "مائة ريال وريال واحد"); 101,000 as "مائة وواحد ألف"; feminine 8 written "ثماني" in compounds ("ثماني وعشرون"; some write "ثمانٍ"); the euro's forms (يوروان، يوروهات); فلوس as the plural of fils; whether bilingual Gulf invoices should drop the adjective on halalas; and "صفر ريال سعودي" for zero.
- Dutch "duizend een" for 1,001 and "een miljoen" (not "één miljoen").
- German "eins" at the end after millions ("eine Million eins Euro").
- French "trente et un mille" with a feminine currency (we don't make it "trente et une mille livres").
- Whether UK and Gulf invoices (not cheques) want "only" at the end.
- Arabic theme names (Ctrl+K → Theme;
// ar: check (theme name)inar.ts): الدفتر (Ledger), المحاسب القانوني (Chartered), تنفيذي (Executive), الطرفية (Terminal), حيوي (Fresh), الراحة (Comfort), إقليمي (Regional). Also check the regional palette names that go with them: الكركم والنيلي (haldi and neel), الزمردي والرملي (emerald and sand), الكحلي التراثي والأحمر الطوبي (heritage navy and brick), أحمر اللك والأزرق المخضر (lacquer red and teal).
Hijri dates¶
Ctrl+K (Language) has a Show Hijri dates row (إظهار التاريخ الهجري).
When it is on, every date the app shows through dates.ts show() is
followed by the Umm al-Qura date: "21-Oct-2026 (10 Jumada I 1448 AH)",
"21-أكتوبر-2026 (10 جمادى الأولى 1448 هـ)".
- The choice is kept like the language: for this computer and for the
signed-in user (
khataos.hijriandkhataos.hijri:<company>:<user>in the webview's storage). Screens already open change when opened again. - The calendar comes from the webview:
Intl.DateTimeFormatwithar-SA-u-ca-islamic-umalqura-nu-latn(en-u-ca-islamic-umalqura-nu-latnin English), Latin digits as everywhere else. WebKit (macOS, iOS) and WebView2 (Windows, Chromium) both have it; an engine without it shows the Gregorian date only, never a wrong one. - Display only. Dates are typed, stored, exported and synced as Gregorian
ISO dates; a shown date fed back into a date field drops the bracket
(
parseUser). Nothing Hijri is ever written into the books. - Printing: Rust has no Hijri calendar, on purpose. The voucher screen
formats the voucher's saved date with the same
Intlcalendar (in Arabic) and passes the text ashijritoprint_voucher; it prints under the date on KSA and UAE bilingual invoices and on Arabic vouchers. With Hijri dates off, nothing is passed and nothing prints. Report prints don't carry Hijri dates.
Messages from Rust¶
Rust writes its messages in English and keeps doing so (logs, the server,
tests). The English text is the message id: src/locales/ar-errors.ts
holds the Arabic for about 85 exact messages (no company open, sign in
first, pick the country first, unknown ledger, khata-core's voucher and
book errors…) and about 40 patterns with variable parts:
["{name} is signed in as {role}, which can't do this; it needs {need}", "{name} مسجّل الدخول بصفة {role}، وهي لا تسمح بهذا؛ يتطلب ذلك صلاحية {need}."],
['a ledger named "{name}" already exists', "يوجد حساب باسم «{name}» مسبقًا."],
["line {n}: {reason}", "السطر {n}: {reason}"],
{reason},{e}and{m}are messages themselves and are translated in turn;{role}and{need}are role labels; any other placeholder is data, shown as written inside a first-strong isolate. A count can use a plural block ("Received {n} change(s)": تغييران, 3 تغييرات, 11 تغييرًا).- The shell's message display (
shell.ts toast) uses its own table first, thenarabicMessage(), then the English, left to right, as before. - A Rust test (
export.rs,the_arabic_message_table_matches_rust) readsar-errors.tsand fails when an English key, or a fixed piece of a pattern, no longer appears in the Rust sources: reword a message in Rust and the test points at the entry to update. - To add a message, copy the English exactly as Rust writes it (a Debug-formatted name comes with its quotes), add the Arabic, and run the desktop tests.
Still English, in the right-to-left layout¶
- Names from the books, including the predefined groups and ledgers a new company starts with (Sundry Debtors, Cash-in-Hand, Profit & Loss A/c…). They are data: renaming them is the user's choice, and a translated chart of accounts for new Arabic companies would be a separate change in the country pack.
- Less common messages from Rust: country-specific errors (MTD, IRP,
CFDI, XRechnung, TDS), warnings listed inside reports (GST, VAT,
Schedule III, e-invoice problems, import skip reasons, rebase reasons),
and OS errors inside "cannot read …". Add them to
ar-errors.tsas needed. - TDS report bodies (
tds.ts) and the newer country screens (zatca.ts,mtd.ts,irp.ts,vat_de.ts,tax_us.ts,tax_mx.ts): check them withpnpm strings --missingand a run in Arabic. - Printed documents: Schedule III statements, cheques and return prints, and the amount in words on Arabic vouchers.