Aller au contenu principal

Internationalisation

Chaque libellé que le framework affiche — entrées de menu, titres d'écran, libellés de colonnes du dictionnaire, boutons de dialogue, messages d'erreur — passe par un chemin de résolution unique : la langue active, choisie par requête parmi trois sources. Le même chemin couvre le frontend React (chrome de l'UI traduit via react-i18next) et le backend (libellés du dictionnaire résolus côté serveur et renvoyés avec la charge utile de l'API).

Cette page décrit l'ordre de résolution, le format d'un pack de langue, la façon d'ajouter une nouvelle langue et la façon de surcharger les libellés par app ou par environnement.


Vue d'ensemble

Ordre de résolution de la langue — la première correspondance l'emporte1 · EN-TÊTE DE REQUÊTEX-Liberty-Lang: frpositionné par le frontend2 · PRÉFÉRENCE UTILISATEURusers.alice.lang = "fr"enregistrée sur la fiche utilisateur3 · PAR DÉFAUT[app] default_lang = "en"repli à l'échelle de l'installation

Comment un libellé est résolu

Un libellé dans dictionary.toml porte une valeur par langue :

[columns.invoice_number]
label = { en = "Invoice number", fr = "Numéro de facture", de = "Rechnungsnummer" }

Quand le framework retourne une définition d'écran, il choisit la langue active, recherche le libellé dans la table et sérialise uniquement la chaîne résolue. Une langue absente retombe sur :

  1. La langue active exactement (fr).
  2. La langue active dépouillée de sa région (fr à partir de fr-CA).
  3. La langue par défaut (en si [app] default_lang = "en").
  4. La première langue disponible dans la table.

Un libellé sans table du tout (label = "Invoice number") est retourné tel quel — utilisé pour les libellés techniques qui n'ont pas besoin d'être traduits.

La même résolution est appliquée à description, placeholder, tooltip et à toute autre chaîne destinée à l'utilisateur dans le dictionnaire.


La langue active

SourceQuand elle s'applique
En-tête de requête X-Liberty-LangEnvoyé par le frontend React à chaque appel API. Reflète le sélecteur de langue dans l'en-tête. Prime sur tout le reste.
Préférence utilisateur (users.<name>.lang)Enregistrée sur la fiche utilisateur. Appliquée quand la requête n'a pas d'en-tête — typiquement un appel API direct (CLI, intégration).
[app] default_langLa valeur par défaut à l'échelle de l'installation. Appliquée en l'absence d'en-tête et de préférence utilisateur. Vaut "en" quand non définie.

L'en-tête X-Liberty-Lang prime délibérément sur la préférence utilisateur — un utilisateur connecté en fr qui bascule le sélecteur d'en-tête sur en voit l'interface en anglais sans modifier sa préférence enregistrée.


Langues prises en charge

Le framework est livré avec les traductions d'interface anglais (en) et français (fr). Toute autre langue peut être ajoutée en déposant un pack de langue — voir ci-dessous. L'interface Paramètres liste chaque langue chargée dans le sélecteur d'en-tête ; les langues sont chargées au démarrage du framework.

Les codes ISO 639-1 à deux lettres sont la convention (en, fr, de, es, it, …). Les variantes régionales sont acceptées (fr-CA, en-GB) ; la résolution retombe sur le code de base quand la variante régionale n'est pas définie.


Ajouter une nouvelle langue

Un pack de langue est un fichier JSON par surface. Le framework les attend sous liberty-apps/i18n/<lang>/ :

liberty-apps/
└── i18n/
└── de/
├── common.json ← chrome du framework (boutons, erreurs, dialogues)
├── apps.json ← libellés du sélecteur d'espace de travail
├── billing.json ← surcharges par app
└── crm.json

common.json — chrome du framework

{
"actions": {
"save": "Speichern",
"cancel": "Abbrechen",
"delete": "Löschen",
"edit": "Bearbeiten",
"refresh": "Aktualisieren"
},
"errors": {
"required": "Pflichtfeld",
"invalid_format": "Ungültiges Format"
}
}

Les clés sont stables d'une version à l'autre du framework ; les nouvelles clés arrivent dans les packs en et fr fournis et doivent être traduites à mesure qu'elles sont ajoutées. La CI du framework exporte à chaque livraison le différentiel entre en et chaque autre langue sous forme d'arriéré de traduction.

Surcharges par app

billing.json surcharge tout libellé que l'app billing a déclaré dans dictionary.toml. Utile quand :

  • Une formulation différente est souhaitée pour une seule app sans toucher au dictionnaire central.
  • Une app éditeur packagée ne livre que en et fr ; la traduction de est ajoutée côté installation.
{
"columns.invoice_number.label": "Rechnungsnummer",
"columns.due_date.label": "Fälligkeitsdatum",
"menus.billing.label": "Rechnungsstellung",
"screens.billing.invoices.title": "Rechnungen"
}

Les clés sont le chemin pointé dans le TOML central — le même chemin qu'utilise l'opération Renommer.

Activation

Après dépôt des fichiers, redémarrer le framework (les packs de langue sont chargés une seule fois au démarrage). La page Langues de l'interface Paramètres liste chaque pack détecté avec son nombre de clés ; un pack partiel est admis — les clés manquantes retombent sur la chaîne de résolution.


Priorité des surcharges

Un libellé peut être défini à plusieurs endroits. L'ordre de résolution (le premier l'emporte) :

  1. Pack par app dans liberty-apps/i18n/<lang>/<app>.json.
  2. Table label inline dans l'entrée de dictionnaire de dictionary.toml.
  3. Pack commun dans liberty-apps/i18n/<lang>/common.json (pour le chrome du framework).
  4. Pack fourni dans liberty-next/liberty/i18n/<lang>/*.json (les valeurs par défaut propres au framework).

Un client qui veut renommer le bouton Save en Submit pour une seule app le fait dans liberty-apps/i18n/en/<app>.json sans toucher au framework ni au dictionnaire central :

{ "actions.save": "Submit" }

Accès API aux traductions

Le frontend embarque les traductions pour la langue active au moment du build ; le backend sert les mêmes traductions via GET /api/i18n/<lang> pour les consommateurs à l'exécution (exports Excel, génération PDF, etc.) :

GET /api/i18n/fr
X-Liberty-Lang: fr

→ 200 OK
{
"actions": { "save": "Enregistrer", "cancel": "Annuler", ... },
"errors": { "required": "Champ obligatoire", ... },
"apps": { "billing": "Facturation", ... },
"billing": { "columns.invoice_number.label": "Numéro de facture", ... }
}

La réponse est mise en cache côté serveur et étiquetée par la révision du bundle i18n — les clients ne re-récupèrent que lorsque la révision change (après un POST /admin/reload sur la portée i18n).


Langues de droite à gauche

La couche React du framework respecte l'attribut dir="rtl" quand la langue active est l'une de ar, he, fa, ur. Les icônes adaptées au mirroir, le rembourrage et l'alignement des tableaux basculent automatiquement. Les schémas SVG intégrés aux écrans ne sont pas reflétés — une variante par sens est nécessaire si un diagramme de flux doit se lire de droite à gauche.


Conseils et bonnes pratiques

  • Traduire les entrées de dictionnaire inline, le chrome du framework via les packs. Le dictionnaire est l'endroit où chaque libellé métier se trouve ; les chaînes communes de l'UI appartiennent aux packs partagés.
  • Garder les clés stables. Renommer une entrée de dictionnaire casse chaque surcharge de traduction par app ; utiliser l'opération Renommer de l'interface Paramètres, qui réécrit les packs de langue de manière synchrone.
  • Exécuter l'arriéré de traduction après chaque livraison. Le pack en fourni est la référence ; liberty-admin i18n-diff fr liste chaque clé absente du pack français.
  • Ne pas traduire les libellés techniques. Les noms de colonnes de base de données, les codes d'erreur et les messages de log gardent leur forme uniquement anglaise — ils sont destinés aux opérateurs, pas aux utilisateurs finaux.
  • Tester sous chaque langue. Un libellé allemand long casse les colonnes de tableau serrées ; basculer l'UI en de le détecte.

Pour aller plus loin

  • Apps — sélecteur d'espace de travail qui utilise l'espace de traduction apps.
  • Concepts → Dictionnaire — où les tables inline label = { … } sont déclarées.
  • Référence REST APIGET /api/i18n/<lang> et le contrat de l'en-tête X-Liberty-Lang.