Retour au Blog
Angular4 juillet 202610 min de lecture

Applications bilingues au Canada : défis techniques et UX

DR

Doug Ribeiro

10 min de lecture

J'ai travaillé à distance depuis Montréal pour une banque basée à Toronto, où le bilinguisme n'était pas une demande de fonctionnalité. C'était une exigence de base. Sur ce travail, chaque écran devait tenir la route aussi bien en anglais qu'en français, pas seulement pour les labels traduits, mais pour la conformité légale, le formatage, l'accessibilité et la qualité UX. L'expérience francophone ne peut pas être un citoyen de seconde classe du même codebase.

La plupart des tutoriels i18n couvrent la mécanique des fichiers de traduction. Cet article couvre ce que ces tutoriels ne couvrent pas : les cas limites, les décisions UX qui diffèrent entre les langues, les exigences légales spécifiques aux applications bilingues canadiennes, et les patterns d'implémentation Angular qui ont fonctionné pour moi en production.

La configuration ngx-translate qui passe réellement à l'échelle

ngx-translate est la bibliothèque de traduction vers laquelle je me tourne en Angular, combinée avec des fichiers de traduction chargés en lazy par module de fonctionnalité. Charger toutes les traductions d'emblée dans un fichier JSON monolithique fonctionne pour les petites applications mais devient un problème de performance quand le fichier de traduction grossit. Dans une grande application bancaire avec des centaines d'écrans, le fichier de traduction peut atteindre plusieurs centaines de kilo-octets.

Mon pattern est de découper les traductions par module de fonctionnalité et de les charger en même temps que le code chargé en lazy du module. Chaque fonctionnalité enregistre son propre namespace de traduction avec un resolver sur sa route racine, de sorte que le router retient la navigation jusqu'à l'arrivée des chaînes et que personne ne voit de clés brutes. N'utilisez pas APP_INITIALIZER ici : les app initializers ne s'exécutent qu'une fois au bootstrap, donc un initializer déclaré dans les providers d'une route lazy ne se déclenche jamais. Les traductions racine couvrent les éléments partagés comme la navigation, les labels de boutons communs et les messages d'erreur globaux. Les traductions de fonctionnalité couvrent le contenu spécifique au domaine.

typescript
// Route resolver: the router waits for it before the feature renders
export function featureTranslations(feature: string): ResolveFn<boolean> {
  return () => {
    const translate = inject(TranslateService);
    const http = inject(HttpClient);
    return forkJoin([
      http.get<Record<string, unknown>>(`/assets/i18n/${feature}/en.json`),
      http.get<Record<string, unknown>>(`/assets/i18n/${feature}/fr.json`),
    ]).pipe(
      tap(([en, fr]) => {
        translate.setTranslation('en', en, true); // merge: true
        translate.setTranslation('fr', fr, true);
      }),
      map(() => true)
    );
  };
}

// accounts.routes.ts (lazy loaded via loadChildren)
export const ACCOUNTS_ROUTES: Routes = [
  {
    path: '',
    resolve: { i18n: featureTranslations('accounts') },
    children: [
      // feature routes
    ],
  },
];

// Translation key convention: feature.section.element
// accounts.dashboard.welcomeMessage
// accounts.transactions.emptyState

Formatage des dates et nombres : pas juste un réglage de locale

L'anglais canadien et le français canadien s'accordent sur les dates numériques et divergent sur les dates longues. Les locales en-CA et fr-CA portent toutes deux un format court y-MM-dd dans les données CLDR qu'Angular embarque, donc DatePipe rend 2025-07-13 dans les deux langues. C'est la forme longue qui diffère : July 13, 2025 en anglais, 13 juillet 2025 en français. Cela fait des données de locale la voie sûre et du formatage fait à la main la voie dangereuse. Dès que quelqu'un écrit son propre utilitaire de formatage en MM/JJ, ou affiche telle quelle une date courte au format américain venue d'une réponse d'API, 07/06/2025 se lit 6 juillet pour un lecteur qui attend MM/JJ et 7 juin pour un lecteur qui attend JJ/MM. Sur une date d'échéance de prêt ou une date d'échéance de paiement, cette mauvaise lecture a de vraies conséquences financières. Passez chaque date par DatePipe avec la locale enregistrée et l'ambiguïté n'a jamais l'occasion d'apparaître.

Le DatePipe intégré d'Angular gère correctement le formatage sensible à la locale quand il est configuré proprement, mais cela nécessite d'enregistrer les données de locale explicitement :

typescript
// main.ts
import { registerLocaleData } from '@angular/common';
import localeFrCA from '@angular/common/locales/fr-CA';
import localeEnCA from '@angular/common/locales/en-CA';

registerLocaleData(localeFrCA, 'fr-CA');
registerLocaleData(localeEnCA, 'en-CA');

// In your app config. LOCALE_ID is resolved once, when the injector creates it,
// so this sets the startup locale; it does not follow a runtime language switch
{
  provide: LOCALE_ID,
  useFactory: (languageService: LanguageService) => languageService.currentLocaleId(),
  deps: [LanguageService],
}

// Usage in templates. Pass the current locale explicitly so a language switch
// re-formats without a reload (currentLocale tracks LanguageService)
// {{ transactionDate | date:'longDate':undefined:currentLocale }} → July 13, 2025 (en-CA)
// {{ transactionDate | date:'longDate':undefined:currentLocale }} → 13 juillet 2025 (fr-CA)

// Currency formatting
// {{ amount | currency:'CAD':'symbol-narrow':'1.2-2':currentLocale }}
// en-CA: $1,234.56 | fr-CA: 1 234,56 $

Notez la différence de position du symbole monétaire. En français canadien, le symbole monétaire va après le nombre avec un espace. Ce n'est pas juste stylistique. C'est la convention établie que les utilisateurs francophones attendent. Se tromper paraît amateur pour un utilisateur bilingue.

Conformité légale pour les interfaces bilingues au Canada

Les institutions fédérales canadiennes sont soumises à la Loi sur les langues officielles, qui exige que les services soient disponibles dans les deux langues officielles avec une qualité égale. Les banques ne sont pas des institutions fédérales, mais la Loi sur l'utilisation du français au sein des entreprises privées de compétence fédérale (adoptée en 2023, pas encore en vigueur) donnera aux consommateurs du Québec et des régions à forte présence francophone le droit d'être servis en français par les entreprises de compétence fédérale, banques comprises. Pour les produits numériques, cela a des implications spécifiques qui vont au-delà d'avoir un bouton de changement de langue.

Chaque communication destinée aux utilisateurs doit être disponible dans les deux langues simultanément ou sur demande. Cela inclut : les messages d'erreur (y compris les erreurs côté serveur), les divulgations légales et conditions, les emails de notification, les documents PDF générés par l'application, et les annonces d'accessibilité faites aux lecteurs d'écran. Il ne suffit pas de traduire l'interface si le PDF généré par l'interface n'est qu'en anglais.

En pratique, le contenu bilingue doit être maintenu pour chaque type de contenu qu'un parcours touche. La règle que je recommande, et c'est ma recommandation plutôt que la description du processus d'un employeur : le contenu n'est pas terminé tant que les deux versions linguistiques ne sont pas complètes, et c'est appliqué au niveau du workflow, pas juste comme convention. Côté front-end, demandez le contenu à l'API avec un paramètre de langue et attendez une réponse complète dans la langue demandée.

L'expansion du texte : le problème de layout caché

Le texte français est plus long que son équivalent anglais : environ 20 % à 30 % sur du texte courant, et souvent bien davantage sur les labels courts. C'est un défi i18n bien documenté, mais ses implications pratiques dans le design d'interfaces financières sont sous-estimées. Un label 'Account balance' en anglais devient 'Solde du compte' en français. Longueur similaire dans ce cas. Mais 'View transaction details' devient 'Afficher les détails de la transaction', soit presque 60 % plus long. Dans un tableau de bord compact avec un espacement serré des boutons, cette différence de longueur cause des problèmes de layout.

AnglaisSubmit → 6 caractèresView details → 12 caractèresAccount settings → 16 caractèresTransaction history → 19 caract…Forgot your password? → 21 cara…Next payment due → 16 caractèresFrançais canadienSoumettre → 9 caractères (+50%)Afficher les détails → 20 carac…Paramètres du compte → 20 carac…Historique des transactions → 2…Mot de passe oublié? → 20 carac…Prochain paiement dû → 20 carac…VS

Mon approche de design pour les applications bilingues canadiennes est de concevoir d'abord en français pour les composants riches en texte. Si le layout fonctionne en français, il fonctionne presque toujours en anglais. L'inverse n'est pas vrai. Un layout optimisé pour l'anglais cassera fréquemment sous le texte français.

En pratique, j'utilise des techniques CSS qui gèrent l'expansion de texte gracieusement : des layouts basés sur flex avec troncature par ellipse quand c'est approprié, des contraintes min-width sur les éléments bouton plutôt que des largeurs fixes, et des container queries sur les composants card pour refluer le contenu quand le ratio texte/conteneur devient défavorable.

Différences idiomatiques au-delà de la traduction mot à mot

La traduction n'est pas un remplacement mot à mot. Le français financier au Canada a des conventions terminologiques spécifiques qui diffèrent à la fois du français général et du français européen. Je ne tranche pas ces questions avec mon propre français. Je consulte l'Office québécois de la langue française. Sa Vitrine linguistique donne compte chèques comme terme canadien pour un chequing account, alors que compte courant sonne comme du français européen pour un utilisateur québécois. Utiliser le mauvais terme régional signale à cet utilisateur que l'interface n'a pas été écrite pour lui.

C'est pourquoi je fais passer les traductions financières par un traducteur professionnel spécialisé en français canadien, suivi d'une revue par un locuteur natif du français québécois. C'est la chaîne que je recommande, pas le récit de la façon dont un employeur la met en place. La traduction automatique, même de haute qualité, produit systématiquement des expressions de français européen pour la terminologie financière. Dans un contexte réglementaire québécois, ce n'est pas acceptable.

Les conventions de ton et de formalité diffèrent aussi. Les interfaces financières en anglais canadien utilisent un langage relativement décontracté et direct, 'You owe' plutôt que 'Your outstanding balance is'. Les interfaces financières en français canadien sont plus formelles, et une formulation comme 'Le montant dû est' se rapproche davantage du registre de la terminologie financière de l'OQLF que ne le fait une traduction littérale de la version anglaise décontractée. C'est un choix qui revient à votre traducteur et aux fiches de l'OQLF, pas au développeur front-end. C'est une décision de stratégie de contenu qui affecte toute l'interface.

UX du sélecteur de langue : persistant, prévisible, accessible

Le sélecteur de langue est une décision de design UX qui porte plus d'implications qu'il n'y paraît. Les conventions qui valent la peine d'être suivies :

  • Le sélecteur de langue devrait toujours afficher la langue vers laquelle vous basculerez, pas la langue dans laquelle vous êtes. Si l'interface est en anglais, le sélecteur dit 'Français'. Si c'est en français, il dit 'English'. C'est le standard du gouvernement canadien et la convention attendue par les utilisateurs bilingues.
  • La préférence de langue devrait persister entre les sessions quand l'utilisateur est authentifié. La stocker dans un profil utilisateur côté serveur plutôt que dans le stockage du navigateur assure la cohérence entre les appareils.
  • Les changements de langue devraient prendre effet immédiatement sans rechargement complet de page autant que possible. En Angular, cela signifie que le changement du TranslateService se propage de manière réactive dans l'arbre de composants sans navigation.
  • Le sélecteur de langue doit être atteignable au clavier et annoncé correctement par les lecteurs d'écran. Ce n'est pas un bouton qui effectue une action dans la page courante. Il change la langue de toute l'interface, ce qui est un changement d'état suffisamment significatif pour justifier une annonce aria-live.
  • Placez le sélecteur de langue de manière cohérente dans un emplacement prévisible. Dans les designs inspirés du gouvernement canadien, le coin supérieur droit du header est la convention établie. Ne le déplacez pas dans le footer ou ne l'enterrez pas dans un menu de paramètres.

Gestion du contenu dynamique et du contenu généré par l'utilisateur

Les labels d'interface statiques sont la partie facile. Le problème plus difficile est le contenu généré par l'utilisateur et les données dynamiques. Si un utilisateur entre son adresse en français, la version anglaise de l'interface l'affiche-t-elle correctement ? Si un conseiller financier écrit une note en anglais, et qu'un client francophone la lit via l'interface française, que se passe-t-il ?

Il n'y a pas de solution technique propre à cela. La réponse honnête est que le contenu généré par l'utilisateur est neutre linguistiquement. Il est stocké et affiché tel que saisi, sans traduction. Ce que vous pouvez contrôler, ce sont les éléments d'interface environnants et le libellé de ce contenu. Vous pouvez aussi, dans les contextes appropriés, fournir la traduction automatique comme fonctionnalité d'assistance avec une indication claire qu'il s'agit d'une traduction automatique et non officielle.

Pour le contenu généré par le système. Messages de statut de la demande, notifications automatiques, erreurs de validation. Le contenu devrait toujours être disponible dans les deux langues. Un pattern qui fonctionne : stocker le contenu système comme les codes de statut et les documents requis avec une description anglaise et une description française, et laisser la couche d'affichage sélectionner la langue en fonction de la préférence utilisateur courante. Cela nécessite que le modèle de données back-end soit conçu avec le bilinguisme en tête dès le départ, pas ajouté après coup.

Planification du RTL : pérenniser sans sur-construire

Le Canada n'a pas d'exigences de langue RTL de la même manière que les applications ciblant les marchés arabe ou hébreu. Cependant, si vous construisez une bibliothèque de composants pour une grande institution financière opérant à l'international, il vaut la peine de prendre des décisions compatibles RTL dès le départ plutôt que de les ajouter après.

L'implication pratique pour les applications canadiennes EN/FR est d'utiliser des propriétés CSS logiques (margin-inline-start au lieu de margin-left, padding-inline-end au lieu de padding-right) dans les styles de composants partagés, et de s'assurer que l'attribut dir est défini correctement sur l'élément html quand la langue change. Ces habitudes coûtent très peu à établir et fournissent une optionnalité significative pour une internationalisation future.

Le bilinguisme dans les logiciels canadiens n'est pas une fonctionnalité. C'est le produit minimum viable pour servir la population du pays.

Construire des applications véritablement bilingues, pas juste traduites, nécessite de traiter l'EN et le FR comme des préoccupations de design co-égales dès le premier jour d'un projet. L'implémentation technique est simple une fois que les fondations de design et de stratégie de contenu sont correctes. Le travail difficile est organisationnel : s'assurer que chaque membre de l'équipe, du product owner au développeur back-end, comprend que le français n'est pas une réflexion après coup.

Références

i18nCanadaAngularUX/UIngx-translate