Locale-aware rendering
Read the host's locale, flip RTL, render translations, react to mid-session locale changes. ~5 minutes.
The i99dash host runs in en or ar. Arabic flips the page direction
to RTL — and gets it wrong if you forget. This recipe is the smallest
correct version: read the locale, set lang + dir, render the right
strings, refresh on change.
Step 1 — read locale once on mount
import { MiniAppClient } from '@i99dash/sdk';
const client = MiniAppClient.fromWindow();
const ctx = await client.getContext();
document.documentElement.lang = ctx.locale;
document.documentElement.dir = ctx.locale === 'ar' ? 'rtl' : 'ltr';Setting lang and dir on <html> cascades:
- CSS
[dir="rtl"]selectors flip layout. - Logical properties (
margin-inline-start,padding-inline-end) resolve correctly. <input>text alignment matches the language without any code.
Step 2 — translation table
const STRINGS = {
en: {
title: 'Fuel near you',
loading: 'Loading…',
empty: 'No stations.',
retry: 'Retry',
},
ar: {
title: 'الوقود قريب منك',
loading: 'جارٍ التحميل…',
empty: 'لا توجد محطات.',
retry: 'إعادة المحاولة',
},
} as const;
type Locale = keyof typeof STRINGS;
export function t(locale: Locale, key: keyof typeof STRINGS.en): string {
return STRINGS[locale][key] ?? STRINGS.en[key];
}Why a flat table not i18next / react-intl for this size: bundles
ship to constrained head-units. A 50-key flat table is < 1 KB; a
proper i18n library is 30–200 KB. Use the library only when you have
hundreds of strings and pluralisation rules.
Step 3 — react to mid-session locale changes
Currently, getContext() is the only way to read locale, and the
host doesn't push an event when the user changes it system-wide. The
next launch picks up the new value.
For dev-mode iteration: the dev-server's control panel
(http://127.0.0.1:5173/_sdk/ui) lets you flip locale live. Your
code re-reads getContext() on the next call, so a refresh shows
the updated language.
async function refreshLocale() {
const ctx = await client.getContext();
document.documentElement.lang = ctx.locale;
document.documentElement.dir = ctx.locale === 'ar' ? 'rtl' : 'ltr';
rerender(ctx.locale);
}
// Re-read on every visibility change (cheap; one bridge call)
document.addEventListener('visibilitychange', () => {
if (!document.hidden) void refreshLocale();
});Step 4 — RTL-friendly CSS
Logical properties make almost everything flip automatically:
.station {
/* ❌ Will not flip in RTL */
/* margin-left: 12px; padding-right: 8px; text-align: left; */
/* ✅ Flips in RTL */
margin-inline-start: 12px;
padding-inline-end: 8px;
text-align: start;
}Use direction-aware utilities for icons and arrows:
.chevron {
/* ❌ Always points right */
/* transform: rotate(0); */
/* ✅ Points "forward" in both languages */
transform: rotate(0);
}
[dir="rtl"] .chevron { transform: rotate(180deg); }Or with logical properties:
.chevron::after {
content: "›"; /* unicode handles direction in fonts that support it */
}Tailwind shortcut: ltr: / rtl: variants for things logical
properties can't express:
<button class="ltr:rounded-l-md rtl:rounded-r-md">Continue</button>Step 5 — number, date, currency
Always use the platform Intl APIs with the locale you read from
getContext(). They handle digit shaping (Latin vs Arabic-Indic),
decimal separator (. vs ٫), and direction:
const fmt = new Intl.NumberFormat(ctx.locale, {
style: 'currency',
currency: 'SAR',
minimumFractionDigits: 2,
});
fmt.format(2.33);
// en: "SAR 2.33"
// ar: "٢٫٣٣ ر.س."Common mistakes
| Mistake | Why it bites |
|---|---|
Using margin-left instead of margin-inline-start | Layout doesn't flip in RTL; site looks broken on Arabic |
Hard-coding text-align: left | Same — text goes the wrong way |
Forgetting dir attribute on <html> | Browser doesn't know the direction; logical properties fall back to LTR |
| Concatenating translated strings | t('greet') + ' ' + name works in English but Arabic word order is different. Use template strings with positional placeholders, or a real i18n library |
Using toLocaleString() without locale | Defaults to runtime locale, which may differ from the host's UI locale |
Test it
Open the dev-server's control panel
(http://127.0.0.1:5173/_sdk/ui), flip Locale: en → ar, refresh
your page. Verify:
-
<html dir>is nowrtl. - Layout flipped (text on the right, icons mirrored).
- Numbers render with Arabic digits (
٢not2) where the currency style includes them. - No left/right wording in copy ("see below" beats "see right" — "right" doesn't translate predictably).
Related
MiniAppContext.locale— the field on the schema.- Best practices — privacy — for user-id handling alongside locale.
- MDN: CSS logical properties — exhaustive reference.