How We Ship Bilingual (Bangla + English) ERPs Without Losing Sanity
The database choices, i18n patterns, and font stack that keep a bilingual ERP legible on a warehouse tablet and a boardroom laptop.

Every ERP we ship in Bangladesh runs bilingual. Not "translated" — bilingual. The warehouse floor reads Bangla; the finance director reads English; the same invoice must render correctly in both. Here is how we architect that so it does not become a tax on every future feature.
**The single hardest decision: one column or two**
Every user-facing string that varies by language lives in a `_translations` table, keyed on (parent_id, locale). Not a JSON column with locale-keyed values. Not a duplicate row per locale in the parent. A separate normalised child table. The reason is boring but decisive: a partial translation is the common case, not the exception, and only the child-table shape lets a finance manager add the English version of a product name six months after operations shipped the Bangla one, without touching the row anyone else is editing.
**The base locale rule**
Every row has a required English translation. That is the fallback the reader falls back to when a Bangla row is missing. It is also the language the API returns to a foreign-owned integration when nothing else is specified. In four years of shipping this pattern, we have never regretted making English the base and Bangla the optional overlay — the opposite is briefly tempting for a Dhaka-only client and then always painful when they open a Chittagong branch that hires an Indian consultant.
**Fonts: the trap you only find in production**
The Bangla glyphs your designer picked in Figma render correctly at 32 pixels and turn to soup at 12. Every ERP has 12-pixel figures — line items on an invoice, cell contents on a stock report, badge labels. We stopped fighting this and settled: Noto Sans Bengali for body copy, Noto Sans Bengali UI for anything under 14 pixels, subset to the CJK+Bengali glyph range only, self-hosted on the same origin. That last part cuts render time by 700ms on a 3G tether, which is what most warehouse tablets in Bangladesh actually run on.
**Number formatting — not as simple as `toLocaleString`**
Bengali numerals (০–৯) are correct on a receipt and wrong on a spreadsheet export the accountant will paste into Excel. Currency amounts in Bangla financial statements typically use lakh and crore groupings (12,34,567) rather than thousand groupings (1,234,567). We settled on: numerals rendered per-locale via a `formatNumber(value, locale)` helper, currency amounts always with the lakh/crore comma grouping regardless of UI language (because the National Board of Revenue expects it that way), and everything server-rendered so a browser locale mismatch cannot corrupt the numbers on a printed challan.
**Dates**
Two calendars, four calendar concerns: Gregorian, Hijri, Bangla New Year cycle for cultural events, and the fiscal year that starts 1 July. We render Gregorian on every UI, Bangla month names when the language is Bangla, and store everything as UTC ISO strings in the database. Fiscal year is a report-level derivation, never a stored calendar.
**RTL isn't a factor — until it is**
Neither language is right-to-left, so most of the CSS work is straightforward. The one place we still hit issues: mixed Bangla text with English brand names ("bKash", "Nagad") in a sentence. Chrome and Safari occasionally invert the whitespace around the Latin word. The fix is a `dir="ltr"` wrapper around each proper noun; ugly, but the alternative is a receipt that reads "থেকে bKash পেমেন্ট" as "পেমেন্ট bKash থেকে" on iOS 18.
Bilingual is not translation. It is a data-modelling choice, a typography choice, and a number-formatting choice, made at architecture time. Making it at product-launch time is what turns a three-month project into a nine-month one.
