Langues, libellés et réglages
Ce que ça résout
Deux besoins que chaque application reposait autrement :
- les textes intégrés : les composants affichent des chaînes que personne ne leur passe (« Chargement », « Aucune donnée », « Période suivante », l'
aria-labeld'un bouton). Il faut les avoir en français, en anglais, ou dans la langue de l'application, sans dépendre d'une librairie d'i18n ; - les valeurs par défaut : « chez moi, les boutons sont
outline» ne devrait pas s'écrire sur trois cents boutons. Voir Valeurs par défaut des composants.
Les deux passent par le même fournisseur, SiaProvider.
Deux langues livrées
import { ENGLISH, FRENCH } from "@sia-ui/headless";
import { SiaProvider } from "@sia-ui/react";
<SiaProvider locale={ENGLISH}>
<App />
</SiaProvider>Sans locale, c'est FRENCH. Une locale porte deux choses :
- les textes de tous les composants ;
- la langue de formatage,
language("fr-FR","en-US") : dates, nombres, noms des jours et des mois, pluriels. Un composant qui a une proplocale(unDatePicker, unEventCalendar) la prend par défaut. LeSiaProviderpose aussi l'attributlangsur son élément, pour les lecteurs d'écran et la césure.
Aucun composant n'a besoin d'une prop pour changer de langue : la story Fondations / ConfigProvider / Langue montre le même écran dans les deux.
Le format d'une locale
SiaLocale est un objet typé, entier, comme un thème :
const locale: SiaLocale = {
language: "fr-FR",
// Le vocabulaire commun, au premier niveau.
loading: "Chargement",
close: "Fermer",
previous: "Précédent",
// …
// Un groupe par composant qui a son propre vocabulaire.
eventCalendar: { previousPeriod: "Période précédente", /* … */ },
jsonView: { keys_one: "{count} clé", keys_other: "{count} clés", /* … */ },
// …
};Trois règles rendent ce format compatible avec un fichier de traduction :
- que des chaînes, jamais de fonction : la locale peut venir d'un JSON ;
- les valeurs par leur nom :
"{count} autres", remplacé parformatMessage; - les pluriels à la manière d'i18next : une clé
keysse décline enkeys_one,keys_other(etkeys_few,keys_manypour les langues qui en ont).plural()choisit la forme avecIntl.PluralRulesde la langue.
Les types de chaque groupe sont exportés (EventCalendarMessages, LogStreamMessages…) : une traduction incomplète est une erreur de compilation. Un test vérifie aussi que FRENCH et ENGLISH ont exactement les mêmes clés et les mêmes variables ({count}, {name}).
Les groupes
Le vocabulaire commun compte 47 clés (loading, close, save, copy, yes…). 43 composants ont en plus leur propre groupe, un fichier chacun dans packages/headless/src/locale/ :
| Domaine | Groupes |
|---|---|
| Listes et pages | crudPage, dataTable, filtersBar, pagination, resource, confirmDialog, unsavedChangesGuard |
| Dates et temps | calendar, timePicker, dateTimePicker, durationDisplay, eventCalendar, eventManager |
| Traçabilité | activityLog, auditMeta, entityMeta |
| Saisie | selectField, multiSelect, tagsInput, otpInput, passwordInput, searchInput, slider, transfer, tree, fileUpload, imageUpload, jsonEditor, markdownEditor, richTextEditor, secretFields, rating |
| Application | appShell, accountMenu, avatar, colorModeToggle, commandPalette, tour, qrCode |
| Opérations | jobProgress, logStream, jsonView, liveIndicator |
Côté API, statusMessages prend FRENCH_STATUS_MESSAGES ou ENGLISH_STATUS_MESSAGES (@sia-ui/api). Voir Module API.
Brancher sa propre i18n
Une application qui a déjà i18next, FormatJS ou ses propres fichiers remplit le même format et le passe. SIA UI ne dépend d'aucune librairie de traduction.
import { ENGLISH, type SiaLocale } from "@sia-ui/headless";
import { useTranslation } from "react-i18next";
function Racine({ children }) {
const { t, i18n } = useTranslation();
// Tout l'objet `sia` du fichier de traduction, au format SiaLocale.
const locale = t("sia", { returnObjects: true }) as SiaLocale;
return (
<SiaProvider locale={{ ...locale, language: i18n.language }}>
{children}
</SiaProvider>
);
}Pour une troisième langue, on part d'une des deux livrées : ce qui manque garde la valeur de celle-ci.
<SiaProvider locale={{ ...ENGLISH, ...mesTraductionsAllemandes, language: "de-DE" }}>Remplacer quelques textes
Une locale partielle suffit : SiaProvider fusionne sur ce qui est au-dessus de lui, groupe par groupe. Changer un libellé du calendrier ne fait pas perdre les autres.
<SiaProvider locale={{ empty: "Rien ici", eventCalendar: { today: "Maintenant" } }}>Et une prop passée à l'appel gagne toujours : la locale ne fixe que le défaut.
<JsonView copyLabel="Copier la réponse" />
<EventCalendar labels={{ today: "Ce jour" }} />Un seul fournisseur, imbricable
SiaProvider se pose une fois à la racine. Imbriqué, il ne remplace que ce qu'on lui passe pour la section qu'il entoure : une locale, des valeurs par défaut, un thème ou un mode forcé. Sans theme, colorMode ni className, il n'ajoute aucun élément au DOM.
<SiaProvider locale={FRENCH}>
<App />
<SiaProvider locale={ENGLISH}>
<ApercuClient />
</SiaProvider>
</SiaProvider>SiaConfigProvider (@sia-ui/headless) est obsolète : c'est la brique sans DOM sur laquelle SiaProvider s'appuie. Il reste exporté le temps d'une migration et sera retiré de l'API publique à la prochaine version majeure.
Écrire un composant
Chaque texte affiché par le composant lui-même (texte, aria-label, title, placeholder, texte réservé aux lecteurs d'écran) vient de la locale. Rien en dur.
- Si le mot existe dans le vocabulaire commun (
close,copy,save,yes…), le lire là. - Sinon, créer le groupe du composant :
packages/headless/src/locale/<composant>.ts, avec son interface<Composant>Messages, sa version française et sa version anglaise. L'inscrire danspackages/headless/src/locale/index.ts(une ligne dans chacune des quatre listes). - Dans le composant,
const locale = useSiaLocale(); la prop de l'appelant garde la priorité :copyLabel ?? locale.jsonView.copy. - Formater avec
locale.language, jamais"fr-FR"en dur.
Le test packages/react-web/src/i18n.test.tsx monte chaque composant exporté sous ENGLISH et échoue s'il reste du français dans son texte ou ses attributs accessibles. Il ne voit que l'état initial : ce qui ne s'affiche qu'une fois ouvert (boîte, menu, fiche) se relit à la main.