Zero-BS Guide to Winning iOS Push Notifications
Chapter 5: How Do I Make My Pushes Speak My Users’ Language?
Covered in chapter 5:
best for ChatGPT, Claude, and Gemini
second-best for Gemini and ChatGPT
second-best for Claude
Includes .md / .docx / .pdf
5.1
5.1 The Payload Reality Check
Every push you send—titles, bodies, custom data, even your “oops” debug flag—must fit inside 4 KB (4096 bytes), or APNs drops it on the floor.
5.1.1 Why Founders Should Care

If you…

Then 4 KB means…

Localization impact

Translate on the server (full strings in every payload)

You’re burning bytes fast—each extra locale is pure size.

Fine for short marketing blurbs; risky for chat or long-form copy.

Use loc-key look-ups on device

Payload stays tiny (just a key + args).

Requires an app update for any new phrase; ignores in-app language toggles.

Lean on a Notification Service Extension

You can ship one slim “ID + args” and fetch text on-device.

Best flexibility, but the final assembled message must still obey 4 KB.

5.1.2 Founder-Level Take-Aways
  • Size disciplines strategy. More locales = bigger payloads; decide early whether you’ll localize on the server, on device, or via extension.

  • Don’t treat 4 KB as elastic. Emoji, right-to-left markers, and UTF-8 multibyte characters eat space faster than you think.

  • Test real-world strings. Have QA push worst-case messages (long German legalese, Arabic RTL, emoji) to verify you stay under the cap—before marketing emails the world.

Dev Handoff - 1: Generating a remote notification
Dev Handoff - 2: Sending notification requests to APNs

Bottom line: 4 KB is your carry-on suitcase—pack smart, or somebody’s favourite language gets left on the tarmac.
5.2
5.2 Pick Your Strategy
APNs gives you three ways to localize: send final strings from the server, embed a loc‑key for the system to look up, or let a Notification Service Extension assemble the message on the device. Each is viable, but they solve different problems:

Strategy

Good for…

Watch for…

Server‑side strings

marketing copy and campaigns where you can treat the push like an email. You know every language up front and can change text without shipping a new app.

Every extra locale adds bytes. Multiple locales in one payload can quickly breach the 4 KB limit.

loc‑key + args

simple apps where the system language matches the app language. You include a key and optional arguments (e.g., username), and iOS looks up the string in your bundled Localizable.strings file.

You must ship translations with the app; changing copy means shipping an update. Cannot honour an in‑app language switch because the OS always uses the device language.

Service extension

advanced apps: you can build messages dynamically, honour in‑app language settings, and even fetch rich media. The extension runs before display and can replace the title, subtitle, and body.

Requires an additional target and App Group; your code must call the completion handler within ~30 seconds or the original message appears.


Founder‑level take‑aways:

  • Your localization strategy is an architectural decision. If your product has a global user base and you want marketing teams to tweak copy on the fly, use server‑side strings. If your app is small and updates are infrequent, loc‑keys are fine. If you allow users to set their own language or need to assemble messages dynamically, invest in a service extension.

  • Don’t conflate messaging strategy with push infrastructure. Server‑side strings can live on a third‑party platform or your own backend; loc‑keys and service extensions are purely iOS mechanisms.

Links for devs:
Dev Handoff - 1: Generating a remote notification
Dev Handoff - 2: UNNotificationServiceExtension
Dev Handoff - 3: Modifying content in newly delivered notifications
Dev Handoff - 4: UNMutableNotificationContent
5.3
5.3 Manage Your Strings Like an Adult
The classic way to provide translations is to ship .lproj directories with Localizable.strings files. Xcode’s newer String Catalog (.xcstrings) format modernizes this workflow. A string catalog centralizes every localized string, supports plural rules, and provides a visual editor. It does not change how loc‑keys work: during build the catalog generates the same underlying resources that the OS reads.

For busy founders the choice boils down to workflow:

  • If your team uses external translation tools and is comfortable editing plain text, Localizable.strings is fine. Each supported language lives in its own folder; the risk is duplicate keys and translation drift.

  • String Catalogs integrate directly with Xcode. Translators can see new or missing keys, and designers can preview plural forms without running the app. They also support exporting to standard formats for translators.
Whatever you choose, loc‑key payloads still refer to the key in your resource; the system finds the translated string at runtime. This means you can swap out the underlying resource format without touching your notification payloads.

Founder‑level take‑aways:

  • Invest in a string catalog if your team maintains many languages or often adds new copy. It will save hours of merge conflicts and lost strings.

  • Either way, remember that every new key must be shipped in a new app release. If your marketing team wants to adjust copy daily, this approach is not enough.

Dev Handoff: UNNotificationContent.title / subtitle / body – developers should ensure the title-loc-key, subtitle-loc-key and loc-key values match entries in the string catalog.
5.4
5.4 Device‑Language ≠ App‑Language
The biggest flaw of the loc‑key approach is that iOS always uses the device’s preferred language. If your product lets users choose a language different from their device, notifications will be wrong.

A UNNotificationServiceExtension solves this by running custom code just before the push is displayed. The extension can read the user’s preference from a shared UserDefaults (via App Groups) and rewrite the notification accordingly.

To make this work:

  • Add a Notification Service Extension target. Xcode can scaffold this; it is a lightweight module that runs in the background.

  • Enable App Groups for both the main app and the extension so they can share data.

  • Write the user’s language choice from the app into a shared UserDefaults entry when they change languages.

  • Include "mutable-content": 1 in the payload. Without this flag, iOS won’t invoke your extension.

  • In the extension’s didReceive(_:withContentHandler:), read the language preference, assemble the proper text (from a local dictionary or remote service), and call the completion handler. You have roughly 30 seconds; otherwise the system will show the original message.

Founder‑level take‑aways:

  • If your product markets itself on personalization—think social apps, ride‑sharing, or e‑commerce—support an in‑app language switch. A service extension is the only way your pushes can respect that choice.

  • Don’t overcomplicate: for notifications that only ever display a timestamp or a simple “new follower” message, the classic loc‑key may suffice.

Dev Handoff: UNNotificationServiceExtension – follow Apple’s template and ensure the extension’s Info.plist sets the NSExtensionPrincipalClass and UNNotificationExtensionCategory.
5.5
5.5 Keep It Lean
Embedding multiple translations directly into a single APNs payload might seem like a shortcut, but it eats into your 4 KB budget. A more scalable pattern is to send an identifier and arguments, then let the service extension fetch or construct the final text. This is sometimes called a Hybrid API Pattern.

Here’s how it works:

1. Lean payload: Instead of sending translated strings, the server sends { "content-id": "new-follower", "args": ["@username"] } alongside the standard aps dictionary and mutable-content: 1.

2. Local or remote lookup: The service extension receives the content ID and fetches the appropriate translation using the user’s language code. For static phrases you can bundle a small JSON file in the app; for dynamic text or frequent updates, call a lightweight API (e.g., https://api.yourapp.com/notifications/translate?content-id=...&lang=...).

3. Assemble: The extension inserts the translated strings into the notification content and delivers it to the user.

This pattern keeps the APNs payload tiny and shifts content updates to your server, avoiding unnecessary app releases. It also prevents the service extension from blowing past the 4 KB limit when multiple languages are included.

Founder‑level take‑aways:

  • Offload the heavy lifting to your API. You can update translations or adjust copy without waiting for an app release.

  • Resist the temptation to include all languages in a single payload; you’ll hit the APNs ceiling and your notification will silently fail.
Dev Handoff: UNNotificationAction – if you include interactive buttons (e.g., “Accept”/“Decline”), localize them via a service extension or by sending multiple UNNotificationAction objects with translated titles. See Apple’s docs on declaring actionable notification types.
5.6
5.6 Test Like You Mean It
Localization bugs hide in the long tail of languages and edge cases. To catch them:

1. Use the simulator: Xcode’s simulator lets you change the system language and cycle through locales. You can then send a test push using a single command: xcrun simctl push booted com.example.MyApp notification.json. The payload must contain an aps dictionary and remain under 4096 bytes.

2. Exercise plural rules: Some languages have up to six plural forms. Create sample notifications with counts of 0, 1, and 2 to verify the correct string appears. With string catalogs you can preview pluralisations in Xcode.

3. Leverage Apple’s Push Notification Console: Apple provides a web‑based tool for sending remote notifications to any of your devices, viewing delivery logs and monitoring status. The console retains a 30‑day history of dispatched notifications, making it easy to debug intermittent issues.

4. Test failure modes: Turn off network connectivity and watch how your service extension behaves. Make sure it times out gracefully and falls back to a default language.

Founder‑level take‑aways:

  • Insist on localization tests during QA. A few hours of rigorous testing will prevent embarrassing push mistakes on launch day.

  • Don’t forget to test right-to-left languages and long strings; German legal terms and emoji have a way of blowing up layouts.

Dev Handoff:
  • xcrun simctl push – Document the command syntax for testers. The .json file must match exactly the shape your server will send.
  • Push Notification Console – Set up access for QA so they can send test pushes to their own devices.
5.7
5.7 The No‑Regret Decision Flowchart
Busy founders shouldn’t agonize over localization architectures. Answer two questions and move on:

1. Do you need to respect an in‑app language preference or assemble dynamic content?

  • Yes: Use a Notification Service Extension with a hybrid API pattern. This solves the “device language ≠ app language” problem and supports rich, dynamic content.
  • No: Continue to the next question.

2. Is your notification copy stable and updated infrequently?

  • Yes: Use the classic loc‑key approach. It keeps payloads tiny and leverages your existing Localizable.strings or String Catalog workflow.
  • No: Use server‑side strings. This lets your marketing team change text without shipping a new app, but watch the 4 KB limit.

That’s it. There’s no wrong choice—only the wrong choice for your product’s lifecycle.
5.8
5.8 Ship‑Day Checklist
Before you push the big red button, ensure your app and backend are ready:

  • Capture the language: The app should persist the user’s preferred language using App Groups (for service extensions) or report it to the backend (for server‑side strings).

  • Set fallbacks: Always provide a default language (usually English) in your payload or service extension. If a translation is missing, fallback gracefully to the default.

  • ICU plurals: Implement plural rules for each language. Remember that some languages have more than two plural forms . Test counts of 0, 1, and >1.

  • Timeout guard: If using a service extension, protect against slow network calls. Call the completion handler with a default message if your API doesn’t respond within a few seconds.

  • Size audit: Verify every type of notification fits under 4 KB, including custom data and emoji.

  • Interactive actions: For actionable notifications, localize button titles and ensure each UNNotificationAction includes translations in your extension or server.

QA sign‑off: Use the simulator and Push Notification Console to test every combination of language, device, and notification type.
5.9
5.9 Beyond APNs: Platform Choices
The strategies above assume you send pushes directly through APNs. Many businesses outsource this to platforms like OneSignal, Airship, Firebase, or Braze. These platforms fall into two camps:

Platform type

Pros

Cons

Built‑in localization (e.g., OneSignal, Airship, Pushwoosh)

Their APIs accept multiple language variants in a single call. The platform detects the user’s language via its SDK and picks the right translation. You don’t need to bundle Localizable.strings in your app. Marketing teams can add or update translations via web dashboards without an app release. OneSignal’s example shows sending contents and headings fields with en, es, and fr keys; unspecified languages fallback to English.

Vendor lock‑in: you depend on the platform’s features and pricing. Switching later requires migrating your translation data and potentially rewriting your push logic. Payload size is still limited by APNs, so don’t embed dozens of languages in the same message.

Standard APNs (e.g., Firebase Cloud Messaging, Amazon SNS, Pusher Beams)

These services act as pass‑through. You implement localization using loc‑keys and possibly a service extension. You retain full control and can migrate providers easily.

More developer work: you must manage language preferences, build translation infrastructure, and ensure plural rules. Some vendors offer templating (e.g., Braze’s Liquid) but you still manage translations yourself.

Hybrid tools (e.g., Braze, CleverTap)

Provide enhanced targeting (locale tags) and templating (Braze’s Liquid) but ultimately rely on Apple’s localization mechanisms. Useful if you already use the platform for analytics or campaigns.

Still require client-side localization code and don’t fully solve the in-app language problem (you must ship .strings or a service extension anyway).


Founder‑level take‑aways:

  • Built‑in localization platforms dramatically simplify multi‑language campaigns and let non‑developers own copy updates. They’re ideal if speed‑to‑market matters more than platform independence.

  • Standard APNs gives you full control and avoids vendor lock‑in at the cost of more engineering effort. For products with long lifecycles or regulatory constraints (e.g., healthcare), this flexibility may outweigh convenience.

  • Hybrid tools add some convenience but don’t replace the need for proper localization code.
Thanks for reading—if this helped, chip in to fund the next guide.
© 2025 Zero-BS Guide to Winning iOS Push Notifications
written by Konstantin Kalinin
Made on
Tilda