Repository navigation
Internationalization
L'interface est disponible en français et en anglais. Le français est la langue de référence : c'est celle dans laquelle les messages sont rédigés en premier, et celle vers laquelle l'application se rabat quand une traduction manque.
Le sélecteur (deux drapeaux) est disponible à deux endroits :
- sur la page de connexion, sous le titre ;
- une fois connecté, dans le menu utilisateur (en haut à droite).
Le choix est stocké dans le localStorage du navigateur, sous la clé locale.
Il est donc propre à un navigateur et à un appareil : il n'est pas rattaché au
compte et n'est pas synchronisé d'une machine à l'autre.
Sans choix explicite, la langue du navigateur (navigator.language) est
utilisée si elle correspond à une langue supportée, sinon le français.
| Élément | Suit la langue de l'interface |
|---|---|
| Textes de l'interface | oui |
| Dates, heures, nombres, tri alphabétique | oui — via Intl, avec la locale complète (fr-FR / en-US) |
| Dates relatives (« il y a 5 min ») | oui — via dayjs |
Attribut lang de la page |
oui |
| Messages d'erreur renvoyés par l'API | oui pour les erreurs migrées ; voir plus bas |
| Noms d'hôtes, tags, conteneurs, chemins | non — ce sont vos données |
| Logs et sorties de commandes des agents | non — texte brut de l'hôte supervisé |
Le serveur renvoie, en plus du message, un code stable (i18nKey) que
l'interface traduit elle-même. C'est nécessaire parce que le navigateur envoie
son propre en-tête Accept-Language, que JavaScript n'a pas le droit de
modifier : sans ce code, une erreur serveur s'afficherait dans la langue du
navigateur et non dans celle choisie dans l'interface.
Tous les sites d'erreur ne sont pas encore migrés. Ceux qui ne le sont pas renvoient un message rédigé en français, affiché tel quel. Cela concerne aujourd'hui les modules Proxmox, suivi de releases, découverte réseau et permissions par hôte.
Le tour complet représente quelques dizaines de lignes de code et la traduction
de ~2 900 messages. Exemple ci-dessous avec l'espagnol (es).
cd frontend/src/locales
mkdir es
cp fr/*.json es/Les fichiers sont découpés par domaine (common, nav, alerts, host, …).
Gardez exactement les mêmes clés que dans fr/ : un test échoue si une clé
existe dans une langue et pas dans une autre.
frontend/src/locales/es.ts, sur le modèle de en.ts : un import par fichier
JSON, puis un export default qui les regroupe. Ce module est ce qui permet à
la langue de tenir dans un seul chunk plutôt qu'un par domaine.
// frontend/src/i18n.ts
export const SUPPORTED_LOCALES = ['fr', 'en', 'es'] as const
const LOCALE_TAGS: Record<SupportedLocale, string> = {
fr: 'fr-FR',
en: 'en-US',
es: 'es-ES', // la région sert aux formats de date et de nombre
}// frontend/src/locales/index.ts
const LOADERS = {
en: () => import('./en'),
es: () => import('./es'),
}// frontend/src/utils/dayjs.ts
import 'dayjs/locale/es'// frontend/src/components/LocaleSwitcher.vue
const FLAGS = {
fr: { labelKey: 'common.languageFrench', flagClass: 'flag-country-fr' },
en: { labelKey: 'common.languageEnglish', flagClass: 'flag-country-gb' },
es: { labelKey: 'common.languageSpanish', flagClass: 'flag-country-es' },
}Le libellé d'une langue s'écrit dans cette langue (Español, pas
« Espagnol ») : quelqu'un bloqué sur une interface qu'il ne lit pas doit
pouvoir reconnaître la sienne. Ajoutez donc languageSpanish avec la même
valeur Español dans toutes les langues.
vue-i18n applique par défaut la règle anglaise (singulier pour 1 uniquement).
Le français met aussi 0 au singulier, d'où la règle déclarée dans i18n.ts :
pluralRules: {
fr: (choice: number) => (Math.abs(choice) <= 1 ? 0 : 1),
},L'espagnol suit la règle anglaise et n'a donc rien à déclarer. Une langue à
plus de deux formes (russe, polonais, arabe…) demande sa propre fonction et
autant de variantes séparées par | dans chaque message concerné.
Les codes d'erreur vivent dans server/internal/apperr/catalog.go. Déclarez la
langue, puis ajoutez sa traduction à chaque entrée :
var SupportedLanguages = []string{"en", "fr", "es"}
var ErrorCatalog = map[string]ErrorMessage{
CodeAdminRequired: {
"en": "admin access required",
"fr": "accès administrateur requis",
"es": "se requiere acceso de administrador",
},
// …
}GetMessage choisit la langue à partir de l'en-tête Accept-Language (en
respectant les facteurs de qualité q=) et se rabat sur l'anglais pour une
langue qu'une entrée ne couvre pas.
cd frontend && npm run lint:i18n && npm run test && npm run typecheck
cd ../server && go test ./internal/apperr/Ces vérifications tournent en CI et bloquent la fusion :
| Vérification | Ce qu'elle empêche |
|---|---|
npm run lint:i18n |
Un texte français écrit en dur dans un .vue ou un .ts au lieu d'être extrait ; un v-html pointé sur une traduction |
src/locales/locales.spec.ts |
Une clé présente dans une langue et absente d'une autre ; un message vide ; des variables {…} qui diffèrent entre deux langues ; une forme de pluriel perdue |
server/internal/apperr/catalog_test.go |
Un code d'erreur sans entrée au catalogue, une entrée sans traduction dans toutes les langues, ou un code absent de errors.json côté interface |
- Une clé se nomme
<domaine>.<clefEnCamelCase>—host.deleteHostConfirmTitle. Les clés enMAJUSCULES_AVEC_UNDERSCORESreprennent un code d'erreur du serveur, celles ensnake_caseun identifiant de métrique ou de module. - Un texte qui affiche un nombre utilise un message au pluriel
(
'{count} hôte | {count} hôtes') et passe le compteur en troisième argument det(), jamais une concaténation. La variable qui pilote le pluriel se nommecount— vue-i18n la reconnaît d'elle-même, donc le message reste correct même si le troisième argument est oublié. Quelques messages gardent un nom plus parlant (created,failed,total) et dépendent alors de cet argument. - Deux compteurs indépendants dans un même message ne fonctionnent pas : vue-i18n ne résout qu'une seule forme de pluriel par message. Composez à partir de deux messages.
- Les dates, nombres et tris passent par
frontend/src/utils/formatters.ts, jamais par une locale écrite en dur. - Les commentaires de code restent en français : ils ne sont pas livrés.
Intégrations
Automatisation
Sauvegardes
Alertes & Monitoring
Compte & Sécurité
Hôtes
Interface