docs
Documentation menu

Localization

Translate a flow and send each user their own language.

#What localization does

Localization lets one flow speak every language your users do. You write your notifications once in a default language, add the languages you want to support, and provide a translation of each message. At send time Pushlane picks the language that matches the recipient's device and delivers that copy — automatically, per user. You do not build a separate flow per language.

Only the notification title and body are localized. Image, deeplink, and the other message fields are shared across every language.

Localization is included from the Growth plan up. On lower plans the panel shows an upgrade prompt in place of the table. See Billing & usage for plans.

#The Localization table

Open a flow in the builder and open the Localization panel from the left rail. It shows a table:

  • Rows — every message in the flow, split into its title and body (one pair per message variant), in the same order as the flow outline.
  • Columns — one per language. A new flow starts with a single column: your default language. Add language adds a column to fill in. The picker lists every language Apple ships iOS in — all 55, written the way you localize an app (zh-Hans, pt-PT, es-419, nb) — and it stays open so you can add a run of languages in one go. You can also type any other locale code (de-CH, sr-Latn); nothing is limited to the list.

The default-language column edits the very same title and body you see in the message inspector — it is the one source of truth for that copy. The extra columns hold the translations. Editing a cell here is the same as editing anywhere else in the builder: it is undoable, works in real-time collaboration, and is saved with the flow.

Heads up
Missing translations are flagged. If you add a language but leave a title or body empty, the flow raises a validation error — the same red chip in the top-right as any other setup issue. Clicking it opens the Localization panel on the empty cell. A localized flow can't go live until every language is filled in, so you never ship a half-translated message.

#Auto-translate

Filling every language by hand is optional. The Auto-translate button at the top of the Localization panel translates your default-language copy into all of your target languages at once, powered by Claude. It is written for push — natural, native-sounding copy at notification length, not a word-for-word machine translation.

  • Auto-translate is part of Localization, so it needs the Growth plan or higher like the rest of the panel.
  • It spends AI credits from your monthly allowance. A run is split into batches of languages — one AI call each — and each batch costs credits, so a 40-language flow costs more than a 3-language one. The button and its menu quote the exact figure before you launch, and the result banner reports what was actually spent. The rate is in Settings → AI credits.
  • If a batch fails — the AI declines the copy, or your credits run out partway — the other batches still land. The result says how many languages were left empty and why; it never reports a partial run as a success.
  • By default it only fills the empty cells, leaving copy you have already written untouched. A Retranslate all option overwrites every cell.
Note
Tokens are preserved. Personalisation tokens like {{ first_name | friend }} are copied into every translation verbatim, so variables keep working in every language. If a translation would drop or mangle a token, Pushlane rejects that cell rather than ship broken personalisation — it stays empty and the missing-translation error points you to it.

#How the language is chosen at send time

Every event a device sends carries its locale — for example fr_FR — in the event context. Pushlane records it as the user attribute locale. When a localized flow reaches a message, the engine resolves the language from that attribute in four steps:

StepRuleResult
1 — ExactThe device locale matches a declared language exactly (fr-FR = fr-FR).That language's copy.
2 — ScriptSame language and same writing system, even when the device does not spell the script out — a Taiwanese zh_TW device is Traditional, a mainland zh_CN device is Simplified.That script's copy.
3 — LanguageThe language matches even when the region differs (fr-CA → fr, or fr-FR). A regional column that covers the device's region wins first (es_MX → es-419).That language's copy.
4 — DefaultNo declared language matches the device.The default-language copy.
Example — a flow with default en + fr + zh-Hans + zh-Hant
device locale "fr_FR"   →  French                 (exact / language match)
device locale "fr-CA"   →  French                 (language match, region ignored)
device locale "zh_TW"   →  Chinese (Traditional)  (script match — never Simplified)
device locale "zh_CN"   →  Chinese (Simplified)   (script match)
device locale "de_DE"   →  English                (no match → default language)
no locale on the user   →  English                (default language)
Note
The two Chineses are two languages. A reader of Traditional Chinese cannot read Simplified. Declare zh-Hans and zh-Hant (and zh-HK if you write Hong Kong copy) as separate columns — Pushlane routes each device to the right script even when its locale only names a region, and never picks one script because it happened to be added first.
Heads up
An uncovered language falls back to the default — never to invented copy. If a user's locale is one you did not add to the flow, they receive the default-language notification: a complete, correct message. Pushlane never machine- translates on the fly at send time and never guesses a translation. Auto-translate is the only thing that writes translated copy, and only into cells you can see and edit before the flow goes live.

#Where the locale comes from

You do not set the locale yourself. Every Pushlane SDK and drop-in reads the device's current locale and sends it in context.locale on each event. Pushlane normalises it (en_FRen-FR) and stores the most recent value as the locale user attribute — no extra SDK call required.

Note
The locale is read from the stored attribute, not from the triggering event's properties — so it resolves correctly even for sends that happen days after the trigger (delays, schedule windows, send-time optimisation). A user has one locale at a time: the most recent one their device reported. This mirrors how personalisation variables are resolved from stored attributes.
Heads up
locale is a reserved system attribute name. You can set it yourself — Pushlane.setAttributes({ locale: 'fr-CA' }) is the supported way to honour an in-app language picker that differs from the OS language. Re-apply it on each app launch: the device projection also writes locale at launch and the most recent write wins, so a one-time override would be reverted by the next session. The value must be a BCP 47 tag (fr, fr-CA, pt-BR…). A non-locale value can't drive language selection: that user falls back to the default language until the device reports a locale again. Never reuse locale (or app_version, os_version, install_env) for unrelated custom data — the device projection and your writes share one value, most recent wins.

Related: Notification templates · How Pushlane works