Esta página ainda não está traduzida no seu idioma: é apresentada em francês.
Traductions
L'interface existe en 16 langues : le français, langue de référence du produit, et les quinze langues les plus parlées. 2765 clés déclinées 16 fois, soit 44240 chaînes, dont la cohérence est tenue par les tests plutôt que par la relecture.
Les langues disponibles#
| Code | Langue | Sens d'écriture |
|---|---|---|
| fr | 🇫🇷 Français référence | gauche à droite |
| en | 🇬🇧 English | gauche à droite |
| es | 🇪🇸 Español | gauche à droite |
| de | 🇩🇪 Deutsch | gauche à droite |
| it | 🇮🇹 Italiano | gauche à droite |
| pt | 🇵🇹 Português | gauche à droite |
| nl | 🇳🇱 Nederlands | gauche à droite |
| pl | 🇵🇱 Polski | gauche à droite |
| ru | 🇷🇺 Русский | gauche à droite |
| tr | 🇹🇷 Türkçe | gauche à droite |
| ar | 🇸🇦 العربية | droite à gauche |
| hi | 🇮🇳 हिन्दी | gauche à droite |
| zh | 🇨🇳 中文 | gauche à droite |
| ja | 🇯🇵 日本語 | gauche à droite |
| ko | 🇰🇷 한국어 | gauche à droite |
| vi | 🇻🇳 Tiếng Việt | gauche à droite |
La langue se choisit par drapeau sur l'écran de connexion — avant même d'avoir un compte — et depuis le profil une fois connecté.
Comment la langue est choisie#
Trois sources, dans cet ordre :
- Le choix du compte, enregistré dans le profil.
- Le cookie, pour un visiteur non connecté qui a cliqué un drapeau sur l'écran de connexion.
- L'en-tête
Accept-Languagedu navigateur.
À défaut, le français.
Une clé absente ne casse jamais la page#
function translate(locale, key, params) {
const dict = dictionaries[locale] || {};
let value = dict[key];
if (value === undefined) value = dictionaries[DEFAULT_LOCALE][key];
if (value === undefined) return key;
…
}
Une traduction manquante retombe sur le français ; une clé inexistante ressort telle quelle. Une traduction incomplète dégrade l'affichage, elle ne casse rien.
Les statuts se traduisent aussi#
C'est le point le moins évident du dispositif. Les statuts — Approuvée,
Brouillon, En attente — sont stockés en français dans la
base. C'est la valeur métier : elle sert aux contraintes CHECK du
schéma, aux comparaisons du code et aux paramètres d'URL. Elle ne doit pas bouger avec la
langue de l'utilisateur.
Mais ce qui s'affiche passe par le dictionnaire. Une table de correspondance
associe chaque valeur stockée à une clé de traduction, et un helper st()
disponible dans toutes les vues fait la conversion au moment du rendu.
function statusLabel(locale, value) {
if (!value) return '';
const key = STATUS_KEYS[value];
return key ? translate(locale, key) : value;
}
| Stocké en base | Clé | 🇬🇧 | 🇩🇪 | 🇯🇵 |
|---|---|---|---|---|
| Approuvée | status.approved | Approved | Genehmigt | 承認済み |
| Brouillon | status.draft | Draft | Entwurf | 下書き |
| En attente | status.pending | Pending | Ausstehend | 保留中 |
| Signé | status.signed | Signed | Unterzeichnet | 署名済み |
167 valeurs de statut sont couvertes, ramenées à 150 clés : les paires de genre du français (Annulé / Annulée, Refusé / Refusée) partagent une traduction, les autres langues ne faisant pas la distinction.
Une valeur absente de la table — un statut ajouté par l'exploitant, par exemple — s'affiche telle quelle plutôt que de laisser une case vide. Mieux vaut un mot français qu'un blanc.
L'arabe retourne réellement la page#
Passer dir="rtl" sur la page ne suffit pas. Le navigateur inverse le texte et
l'ordre des éléments en flex, mais une marge déclarée margin-left reste à
gauche : le liseré de l'onglet actif, la pastille du compte et les marges de tableau se
retrouveraient du mauvais côté.
La feuille de style du produit n'emploie donc que des propriétés logiques :
| Au lieu de | On écrit |
|---|---|
| border-left | border-inline-start |
| padding-right | padding-inline-end |
| margin-right | margin-inline-end |
| text-align: left | text-align: start |
| padding: 0 8px 0 4px | padding-inline: 4px 8px |
Le résultat se vérifie géométriquement plutôt qu'à l'œil : en arabe, le liseré de l'onglet actif passe de 3 px à gauche à 3 px à droite, et la barre latérale passe du bord gauche au bord droit de la fenêtre.
Ce que les tests garantissent#
Un dictionnaire de 44240 chaînes ne se relit pas. Dix-sept tests vérifient à chaque exécution de la suite :
| Vérification | Le défaut qu'elle attrape |
|---|---|
| Parité des clés | Une langue à laquelle on a oublié d'ajouter une clé |
| Aucune valeur vide | Une chaîne laissée en blanc |
Paramètres {nom} identiques | Un paramètre perdu à la traduction : la phrase s'affiche amputée, sans erreur |
| Écritures non latines | Une traduction oubliée se repère à ce qu'elle reste en caractères latins |
| Toute clé appelée existe | Une faute de frappe dans un t('…') |
| Chaque statut pointe vers une clé existante | Une entrée de table qui ne mène nulle part |
| Tout statut du schéma est traduit | Un statut ajouté à une contrainte CHECK sans traduction |
| Aucune vue n'affiche un statut brut | Un <%= x.status %> qui a échappé au helper |
| Aucune vue ne masque le helper | Une boucle forEach((t) => …) qui rend t() inappelable |
| Un statut inconnu ressort tel quel | Une valeur absente du dictionnaire affichée en blanc plutôt qu'en français |
| Un même statut se traduit dans chaque langue | Un statut traduit dans deux langues et oublié dans les quatorze autres |
| Toutes les vues compilent | Un gabarit cassé par une substitution, qui ne tomberait qu'à l'ouverture de la page |
| Les dates suivent la langue | Un 03/04 anglais lu comme le 4 mars — ou l'inverse |
| Le chrome partagé est traduit | Un libellé en dur dans l'en-tête ou la navigation, donc visible partout |
| Le CSS ne fige aucun côté | Un margin-left qui casserait la mise en page arabe |
| Rendu de bout en bout | Une page servie en allemand qui parle encore français |
| Bascule RTL | Une page arabe qui ne passe pas en dir="rtl" |
Une boucle écrite tools.forEach((t) => …) masque le helper de traduction :
à l'intérieur, t('common.edit') tente d'appeler l'objet de la boucle comme une
fonction et la page tombe. Le cas s'est produit ; un test l'interdit désormais dans toutes
les vues.
Où en est la couverture#
Les 89 vues du produit sont traduites, du premier écran de connexion à la dernière boîte de dialogue de confirmation. Les quatre espaces, le chrome présent sur chaque page (en-tête, compte, déconnexion, sélecteur de thème, navigation latérale), tous les statuts, le vocabulaire générique et le vocabulaire propre à chaque métier : comptabilité, paie, trésorerie, qualité, parapheur, sauvegardes, interfaces, données personnelles, parc logiciel, livraisons, événements, points individuels, conformité des tiers, alerte interne, achats, recouvrement et vie juridique.
Trois choses, aucune n'étant du texte affiché : les paramètres d'URL du journal d'audit
(&du=, &au=), l'exemple de commande curl de
la documentation d'API, et un commentaire dans le script qui applique le thème avant le
premier rendu. Traduire un identifiant de requête casserait le filtre ; traduire une
commande la rendrait incopiable.
Deux règles tenues d'un bout à l'autre#
Les valeurs stockées ne bougent pas. Un statut est écrit en français dans la
base : c'est la valeur métier, celle des contraintes CHECK, des comparaisons et
des URL de filtre. Seul l'affichage passe par le dictionnaire. Les attributs
value d'un <select>, les chaînes comparées dans une condition
et les identifiants de filtre gardent leur libellé français — traduire la donnée aurait cassé
le produit dans quinze langues sur seize.
Une phrase coupée par une donnée devient une clé paramétrée. « {count} échec(s) consécutif(s) », « Pagination par {limit} (200 au plus) et {since} », « {total} échéance(s) à 45 jours, dont {overdue} dépassée(s) » : l'ordre des mots n'est pas le même d'une langue à l'autre, et un nombre collé entre deux fragments traduits séparément ne se lit correctement nulle part.
Ajouter ou corriger une traduction#
Les dictionnaires vivent dans src/locales/, un fichier par langue, exportant un
objet plat. Le français est la référence : c'est lui qui définit le jeu de clés.
module.exports = {
'common.save': 'Enregistrer',
'leave.available': 'Solde disponible : {days} jours',
…
};
Pour ajouter une clé, trois règles :
- L'ajouter dans les 16 fichiers — le test de parité échoue sinon.
- Reprendre les mêmes paramètres
{nom}dans chaque langue, quel que soit leur ordre dans la phrase. - Écrire dans l'écriture de la langue — un test refuse une valeur restée en caractères latins pour le russe, l'arabe, le hindi, le chinois, le japonais ou le coréen. Les sigles techniques (
JPEG,PNG) et la marque en sont exemptés.
Pour ajouter un statut, l'inscrire aussi dans la table
STATUS_KEYS de src/i18n.js : un statut déclaré au schéma mais
absent de la table fait échouer les tests plutôt que de ressortir en français chez un
utilisateur étranger.
Le lexique permet de parcourir et chercher l'intégralité des 2765 clés dans les 16 langues.
Documentação do Toutadmin — construída em 2026-09-13. Site autónomo, independente do software.