Translate your app
Translate Pinecart with t(), format money and dates per locale with i18n, fall back along a locale chain, and let mainsdk check catch missing strings.
Status Status is where the work is: Planned (in a milestone, not started), Building (tickets in progress), Preview (usable, may change), then the stability it ships at.
Stability The promise an interface ships with.
- Stable Follows semantic versioning; breaking changes need a new major, 12 months of overlap and a codemod.
- Beta Usable in production; may change before it becomes stable.
- Experimental May change or go away; needs an explicit opt-in in main.yaml.
Show Pinecart in English, Hindi and Tamil, with money and dates formatted for each locale, from one deployment.
Before you start: a MainSDK app with a main.yaml. The calls are described on Languages and formats.
1. Set your locales
i18n:
driver: files # files | tolgee | crowdin | lokalise | ai-draft
locales: [en-IN, hi-IN, ta-IN]2. Write the messages
Messages live in locales/, one file per locale:
orders.count: "{count, plural, one {# order} other {# orders}}"
orders.total: "Total: {total}"orders.count: "{count, plural, one {# ऑर्डर} other {# ऑर्डर}}"
orders.total: "कुल: {total}"Messages use Unicode MessageFormat 2; ICU MessageFormat strings, like the plurals above, are converted when they load. Both cover plurals, genders and selections.
3. Use them in your UI
import { t, i18n } from "mainsdk";
export function orderSummary(orders: { total: number }[]) {
const total = orders.reduce((sum, o) => sum + o.total, 0);
return {
dir: i18n.dir(),
heading: t("orders.count", { count: orders.length }),
total: t("orders.total", { total: i18n.format.money(total, "INR") }),
};
}Render these values in your component, and set dir on its root element.
i18n.format.money(125000, "INR") gives ₹1,25,000.00 in en-IN. Formats come from Unicode CLDR. i18n.format.date() and i18n.format.number() work the same way, and i18n.dir() returns the text direction for right-to-left scripts.
4. Know how the locale is chosen
The user's choice comes first, then their account, then the browser. The locale is never guessed from the IP address alone. Market and locale resolve per request, so one deployment serves every market.
Wrap a handler in i18n.handle(req, () => …, { user }) to run it in the negotiated locale; t() and i18n.format.* then use it.
A missing message falls back along a chain: hi-IN, then hi, then en-IN, then en. Production never shows a key: a message missing everywhere returns an empty string and logs MAIN-I18N-0009. Locale-specific email and notification templates (templates/order.shipped.hi-IN.yaml) are planned.
5. Verify it worked
npx mainsdk checkA missing translation fails the check, not the user: a key missing from a locale under i18n.required is MAIN-I18N-0001, with the file and line of the t() call. Run npx mainsdk i18n pseudo to get long, accented and right-to-left pseudo-locales that catch layout breaks before a translator sees them.
Draft new strings with AI
With the ai-draft driver, npx mainsdk i18n draft --locale hi-IN drafts missing strings through ai, following the glossary in i18n.ai-draft.glossary. Each draft is held as pending in locales/.drafts/; a person approves it with i18n.drafts.approve(id) before any user sees it. An app agent can't approve (MAIN-I18N-0007).
Keep your translators' tools
npx mainsdk i18n export --format xliff and npx mainsdk i18n import <file> move strings to and from XLIFF, iOS .xcstrings, Android strings and ICU JSON. Generating Android values-hi/ and iOS .xcstrings from the same locale files is planned for phase 3.
Next
Was this page helpful?