Ship to Five or More Locales, Automate Localized Push Notifications
Ship to Five or More Locales, Automate Localized Push Notifications

Send loc keys and args, not pre-translated text. The recommended default is app-side key resolution: pass FCM’s title_loc_key/body_loc_key with title_loc_args/body_loc_args, or the iOS equivalents loc-key/loc-args, and let the device resolve them against its own resource files. Reach for a Notification Service Extension or Azure Notification Hubs templates only when you need to fetch or pre-render content server-side at delivery time.
TL;DR:
- App-side resolution is generally more reliable because it checks the device’s current language setting rather than relying on potentially outdated profile data.
- Firebase Cloud Messaging and iOS both resolve localized content through resource keys, requiring proper management of placeholders and resource files for correct rendering.
- Using a Notification Service Extension for rich media or translation updates at delivery time is effective but requires fast, cached fetches to avoid fallback to default language.
- Azure Notification Hubs templates simplify broadcasting in multiple languages but risk delivering outdated templates if not periodically refreshed.
- Automating the management of localized notification keys and translations helps prevent missing or mismatched entries, reducing errors across multiple languages.
Table of Contents
- App-Side vs Server-Side Localized Push Notifications
- Building FCM Payloads With Localization Keys
- iOS: Localizable.strings and APNs Loc Keys
- Android: strings.xml, values- Folders, and Formatting Rules
- When a Notification Service Extension Actually Earns Its Complexity
- Notification Hubs Templates: Broadcasting Without Locale-Specific Messages
- Checklist: Best Practices and Common Pitfalls
- Testing Matrix: Verifying Localized Push on Real Devices
- Fitting Notification Strings Into Your Localization Pipeline
- An Engineering Take on What Actually Holds Up
- How Arkian Speeds Up Shipping Localized Notification Strings
- Sources
App-Side vs Server-Side Localized Push Notifications
App-side resolution means the device holds the translated strings and picks the right one at render time, based on whatever language the phone is actually set to. Server-side pre-localization means your backend decides the language ahead of send time, usually from a locale field stored on the user’s profile, and bakes the final text into the payload before it ever leaves your servers.
The problem with server-side is drift. A user switches their phone to French, but your database still says English, so every notification arrives in the wrong language until someone updates that profile field. App-side resolution sidestips this because it always checks the device’s live setting.
- Default to app-side resolution for transactional and behavioral notifications (order updates, reminders, alerts).
- Use server-side pre-localization only when you need marketing variants, A/B copy tests, or templated broadcasts that a device can’t assemble on its own.
- Never treat a stored profile locale as more authoritative than the device’s current setting.
Building FCM Payloads With Localization Keys
Firebase Cloud Messaging resolves localized content client-side when you send resource keys instead of literal strings. A typical HTTP v1 payload looks like this:
| Field | Example value | Purpose |
|---|---|---|
title_loc_key |
notif_order_shipped_title |
Points to a titled string resource on the device |
body_loc_key |
notif_order_shipped_body |
Points to a body string resource on the device |
title_loc_args |
["Order #4521"] |
Fills placeholders in the title resource |
body_loc_args |
["3", "May 12"] |
Fills placeholders in the body resource |
FCM’s own localization documentation shows the client resolving these keys against local resource files rather than the server dictating final text.
A few things trip up teams here:
- Data payloads (versus notification payloads) hand raw keys straight to your app code, which is useful when you want custom rendering logic beyond what the OS default handler supports.
- Argument order matters and isn’t portable. A key with two placeholders expects args in the exact sequence the resource string defines, and that sequence can differ between iOS and Android versions of the “same” message.
- Localization is not personalization. Keys and resource files handle language; args handle user-specific substitution. Mixing those two concerns in one string resource is a common source of confusing bugs down the line, per Firebase’s own guidance.
iOS: Localizable.strings and APNs Loc Keys
Apple’s push system resolves localized notification content the same way FCM does on Android: through keys, not literal text, matched against locale-specific resource files.
- Store translated strings in
Localizable.stringsfiles inside language-specific.lprojfolders (en.lproj,es.lproj,ja.lproj). - In your APNs payload, set
title-loc-keyandloc-keyto reference those resource entries, and passtitle-loc-args/loc-argsfor any placeholders. - The OS resolves the key against whatever
.lprojfolder matches the device’s active language at the moment the notification displays, per CrossGeeks’ localized push notes.
If you need to fetch updated translations, rich media, or transform content right before display, add a UNNotificationServiceExtension. That’s the mechanism for doing work at delivery time rather than baking everything into the initial payload.
Pro Tip: Keep any network call inside the extension fast and cache-backed. Apple gives extensions a strict execution window, and a slow fetch means the system falls back to your default, untranslated payload text instead of failing gracefully.
Android: strings.xml, values- Folders, and Formatting Rules
Android resolves push localization through the same key-based pattern, pointed at values-<lang> resource directories instead of .lproj folders.
- Store your default strings in
values/strings.xmland translated variants in language-specific folders likevalues-es/strings.xmlorvalues-de/strings.xml. - If a translated string is missing from a locale folder, Android falls back to the default
values/strings.xmlentry rather than showing a blank notification. - Payload keys mirror FCM’s pattern:
title_loc_key,body_loc_key,title_loc_args,body_loc_args, resolved against those XML resources on the device. - Format placeholders using positional specifiers like
%1$sor%2$drather than plain%s, since Android string resources depend on that positional numbering to keep argument order correct across languages with different word order.
Get the folder naming wrong and you’ll see the fallback language silently override your intended translation, which is a frustrating one to debug because nothing actually crashes.
When a Notification Service Extension Actually Earns Its Complexity
A UNNotificationServiceExtension intercepts a push before the OS displays it, giving your code a short window to modify title, body, attachments, or sound. That’s the mechanism, and it’s genuinely useful for a narrow set of cases.
-
Resolving a loc key against a translation cache that’s fresher than what shipped in the last app build.
-
Fetching rich media (an image or updated copy) that wasn’t included in the original payload to keep it small.
-
Handling an edge case where the device locale changed between the last app launch and notification delivery.
-
Extensions run under a tight execution budget, and Apple kills them if they exceed it, so any fetch needs a fast fallback path rather than a blocking network call.
Pro Tip: Design the extension to always show something. If your translation fetch times out, fall back to the original, unmodified payload text rather than dropping the notification entirely.
Notification Hubs Templates: Broadcasting Without Locale-Specific Messages
Azure Notification Hubs takes a different approach: instead of sending one message per language, each device registers a template that maps generic payload properties to its own locale-specific text.
- Devices register a template once, tying incoming property placeholders to the correct local string.
- The hub sends a single locale-agnostic payload, and each device substitutes it into its own registered template at delivery, according to Azure’s tutorial on localized breaking news alerts.
- This avoids tagging every device by language just to broadcast the same event in ten locales, cutting server-side send complexity substantially.
The trade-off is stale templates. If a device’s template doesn’t get refreshed after a language change, the hub happily delivers a broadcast in the wrong language. This pattern shines for high-fan-out use cases, like breaking news or system-wide alerts, less so for one-off transactional messages.
Checklist: Best Practices and Common Pitfalls
- Treat resource keys as stable identifiers. Never rename a key casually; treat it like an API contract, with versioning if the underlying string structure changes.
- Run CI checks that scan for missing keys across every supported language file before merging, so a new message added to English doesn’t silently ship without a Spanish or Japanese counterpart. CrossGeeks documents this exact failure mode as one of the most common causes of empty or malformed notifications.
- Default to device locale resolution over a server-stored profile field, since profile data goes stale the moment a user changes their phone’s language setting.
- Build a graceful fallback for missing translations rather than letting a lookup failure produce a blank notification body.
- Respect frequency capping and consent settings per locale. If you’re operating anywhere near emergency or public-safety messaging, treat that as a distinct system with its own compliance requirements, separate from standard app push, as FEMA’s IPAWS documentation makes clear.
Pro Tip: Mismatched format arguments are the silent killer here. A string expecting two placeholders that receives one renders a raw %2$d right in the user’s notification tray, and it usually ships to production because nobody previewed the actual rendered output before release.
Testing Matrix: Verifying Localized Push on Real Devices
Automated checks catch structural problems early. Lint new string keys for placeholder consistency, run unit tests confirming argument counts match resource expectations, and gate CI so a message can’t merge if any supported language file is missing that key.
Manual QA needs a real matrix, not a spot check on one device:
- Test across at least three or four target locales, including one right-to-left language like Arabic or Hebrew if you support any RTL market, since bidirectional text can break layout in ways left-to-right testing never surfaces.
- Cover multiple OS versions per platform, since older Android versions handle fallback resolution slightly differently than newer ones.
- Include at least one device where you deliberately change the system language after the app was last opened, to catch stale-cache resolution bugs.
- If you use a Notification Service Extension, test the network-timeout fallback path specifically, not just the happy path where the fetch succeeds instantly.
Craft raw test payloads with curl or Postman against your own FCM/APNs credentials so you can verify actual rendered text on device, rather than trusting that the payload structure alone guarantees correct display.
Fitting Notification Strings Into Your Localization Pipeline
Notification keys should live alongside your UI strings in the same versioned resource files, not in a separate ad hoc spreadsheet somebody updates manually before each release. Treat a new notification message the same way you’d treat any new UI string: it needs a key, a default-language entry, and translated entries before it ships.
- Version notification keys the same way you version other localized UI strings, so a rename or removal shows up in code review.
- Automated packaging tools can export ready-built
.strings, Android XML, and JSON artifacts directly into your build pipeline, cutting out manual copy-paste between a translation spreadsheet and your resource folders, which is where a lot of the missing-key errors mentioned earlier actually originate. - Add CI hooks that check placeholder counts and flag any language file missing a key before the packaging step runs, not after.
Pro Tip: If your team ships to five or more locales, manual copy-paste between spreadsheets and resource files is where most missing-key bugs are born. Automating that hand-off with a tool built for Android XML string localization or YAML language files removes the step entirely rather than trying to catch the error after the fact.
An Engineering Take on What Actually Holds Up
App-side resolution wins on correctness, full stop. Server-stored locale fields drift the moment a user changes a setting, and no amount of cron-job syncing fully closes that gap. Save server templates for broadcast-heavy marketing sends where you genuinely can’t push a device-resolved key.
The real maintenance cost isn’t the initial implementation. It’s six months later, when nobody remembers which keys exist, which languages are missing an entry, and why last Tuesday’s release shipped a blank notification to Portuguese users. Stable key names and a CI gate that fails loudly solve that problem before it ships.
— Arkian
How Arkian Speeds Up Shipping Localized Notification Strings
Manually keeping title_loc_key entries synced across a Localizable.strings file, an Android values- tree, and a JSON export for your backend team is exactly the kind of repetitive, error-prone work that eats a release afternoon. A platform automates that hand-off: it generates translated strings, validates placeholder and key completeness across every target language, and packages the result into delivery-ready .strings, Android XML, JSON, or YAML files, without needing repository access or a full translation management system bolted onto your pipeline.

That matters most for small product teams and solo developers who don’t have a dedicated localization engineer checking every release for a missing key or a mismatched argument count. This structured output slots directly into a CI packaging step, so the artifact your build pulls in has already been checked for the gaps that cause blank or malformed notifications in production.
If you’re maintaining notification strings for an iOS app, start with Arkian’s iOS .strings localization tool and see the validated output before it ever touches your build.
Sources
- Localize Messages | Firebase Cloud Messaging - Google
- LocalizedPushNotifications.md — CrossGeeks PushNotificationPlugin
- Use Notification Hubs to send localized breaking news (Azure Notification Hubs tutorial)