Every Canadian product team eventually ships an English-first app and files French as a ticket for later. Later arrives, a translator returns a spreadsheet, and suddenly seventeen screens have text overflowing their containers, a settings toggle whose label wraps to three lines, and a push notification that arrives in the wrong language because someone read the device locale instead of the user's preference.
None of that is a translation problem. All of it is an engineering problem that was deferred, and deferral is what made it expensive.
Here is what treating bilingual support as engineering actually looks like.
The 30% rule
French strings run roughly 15–30% longer than their English equivalents. "Settings" becomes "Paramètres". "Save" becomes "Enregistrer". "Sign out" becomes "Se déconnecter". Individually trivial; collectively fatal to any layout designed against English copy with tight horizontal constraints.
The fix is not to design for French. It is to stop designing against a single fixed string length:
- No fixed-width containers around localized text. Ever.
- Buttons size to content with a defined minimum, not a defined width.
- Multi-line labels get an explicit line limit and a truncation strategy chosen deliberately, not inherited from the framework default.
- Review every screen at the longest supported string, not the shortest.
The cheapest way to enforce this is pseudo-localization. Before any real translation exists, render the app with English strings artificially expanded by ~35% and wrapped in delimiters — [Ŝéttíñgŝ————]. Two things fall out immediately: every layout that cannot survive French, and every string that was hardcoded and therefore did not expand. Run it as a build variant, put it in CI, and screenshot-diff the critical screens.
That single technique catches most of what would otherwise be found by a translator's QA pass three weeks before launch.
Bidirectional and mixed-content edge cases
Even in a purely LTR pair like English and French, mixed content bites:
- Number and currency formatting differ.
1 234,56 $is not$1,234.56, and hardcoded format strings will produce nonsense in one locale or the other. Use the platform formatters. - Date formats differ, and so do abbreviated month names. Never build a date string by concatenation.
- Sorting and search behave differently with accented characters.
émust collate withefor user-facing sort and search, which means locale-aware comparison, not byte comparison.String.localeComparewith the right locale, or the platform collator — not<. - Search input needs accent-insensitive matching, or Quebec users will conclude your search is broken when "cafe" fails to find "café".
Locale is a user property, not a device property
This is the bug that reaches production most often, because it works perfectly in development.
The device locale tells you how to render the UI on that device right now. It does not tell you what language to use for a push notification generated on your server, an email, an SMS, or a PDF receipt. Those are produced server-side, potentially hours later, possibly for a user who has since switched devices.
Store the user's language preference on the user record. Send it up when it changes. Have every server-generated message read from that record. And test the case where a user changes language while a scheduled notification is already queued — the answer should be that they get the new language, and the only way to be sure is to write that test.
String management that survives a second locale
If your localization workflow is "export a spreadsheet, email it, paste it back," you have built a process that fails silently. Missing keys become blank UI. Stale keys accumulate forever. Nobody can tell which strings changed since the last release.
A workable minimum:
- Strings live in the repository, in a structured format, as the single source of truth.
- Keys are semantic (
checkout.button.confirm), never the English text — otherwise every copy edit orphans a translation. - A CI check fails the build on any key present in one locale and missing in another.
- Every string carries a comment giving the translator context.
"Save"is a verb on a button and a noun in a settings header, and a translator without context will guess wrong roughly half the time. - Machine translation is a first draft that a native French reviewer edits. Shipping raw MT into a Quebec-facing product is immediately recognizable and quietly expensive in trust.
What to test in CI
Concretely, the pipeline additions that pay for themselves:
- Pseudo-locale build variant, screenshot-diffed on critical screens.
- Missing-key and orphan-key checks across all locales.
- Snapshot tests for both locales on any screen with dynamic content.
- A lint rule banning string literals in UI components — this is the check that keeps the other four honest.
- One end-to-end test that switches language mid-session and asserts the app does not restart into a broken state.
Why this matters more in Canada than most markets
Two reasons beyond user experience. Quebec's language rules apply to commercial communications and consumer software in ways that can require French to be available on terms at least as favourable as English — this is not a nice-to-have for a product with national reach. And App Store and Play Store listings, screenshots and support content need French versions to rank properly in Quebec, which means localization is an acquisition channel, not just a UI concern.
Treated from sprint one, all of this costs a few percent of build effort. Treated as a pre-launch ticket, it is a redesign wearing a smaller name.
The wider version — Canadian cost bands, PIPEDA and Law 25 architecture, SR&ED eligibility, and how to vet a development partner — is here: Mobile App Development Company Canada: The 2026 Buyer's Guide. Our mobile app development page covers how we structure delivery.
Frequently Asked Questions
How much longer are French strings than English?
Typically 15–30% for UI-length text, with short labels expanding the most in relative terms. Design and review screens at the longest supported string rather than the English original, and use pseudo-localization to surface the failures before a translator ever sees the app.
What is pseudo-localization and why use it?
It is a build variant that renders English strings artificially expanded and wrapped in markers. It exposes both layouts that cannot accommodate longer text and strings that were hardcoded and therefore did not expand — before any translation exists and at effectively zero cost.
Should I use device locale or a stored user preference?
Device locale for rendering the UI on that device; a stored user preference for anything generated server-side, including push notifications, emails and receipts. Relying on device locale for server-generated messages is the most common localization bug that reaches production.
How do I handle accented characters in search and sort?
Use locale-aware collation rather than byte comparison, and make user-facing search accent-insensitive so "cafe" matches "café". Getting this wrong reads to users as a broken search feature rather than a localization gap.
Can I use machine translation for app UI copy?
As a first draft, reviewed and edited by a native speaker before release. Raw machine translation in a Quebec-facing product is recognizable to users immediately and erodes trust in the product generally, not just in the copy.
Does French support affect App Store ranking in Canada?
Yes. Localized store listings, screenshots and keywords materially affect discoverability in Quebec, which makes localization an acquisition investment rather than purely a UI cost.
Top comments (0)