i18n
Chaque champ est multilingue par défaut. Comment les locales façonnent les URL, vers laquelle une requête se résout, et ce qui se passe quand une traduction manque.
Dans cmssy, vous ne modélisez pas les traductions. Chaque champ est déjà multilingue : le contenu est stocké indexé par code de langue, donc une page porte toutes ses langues et une instance de bloc toutes les siennes.
Cela supprime le problème habituel de duplication - pas de « page anglaise » et « page allemande » à synchroniser - et le remplace par deux questions plus petites. Vers quelle locale une requête se résout-elle, et qu'affiche-t-on quand un champ n'est pas encore traduit ?
L'ensemble des locales
Les locales viennent de la configuration du site, pas de votre code :
const { defaultLocale, locales } = await resolveSiteLocales();Ajouter une langue est un réglage d'espace de travail. Votre application la prend en compte à la requête suivante : une nouvelle locale ne demande donc aucun déploiement, seulement des traductions.
URL : la locale par défaut n'a pas de préfixe
C'est la règle dont tout le reste découle :
localizedPath("/pricing", "fr", "fr"); // "/pricing" défaut : nu
localizedPath("/pricing", "de", "fr"); // "/de/pricing" autres : préfixées
localizedPath("/", "de", "fr"); // "/de"Garder la locale par défaut sans préfixe signifie que vos URL canoniques ne changent pas quand vous ajoutez une deuxième langue : liens existants, partages et positions survivent tous.
Résoudre une requête
Le sens inverse lit le premier segment du chemin, et il est volontairement strict :
splitLocaleFromPath(["de", "pricing"], { defaultLocale: "fr", locales: ["fr", "de"] });
// -> { locale: "de", path: ["pricing"] }
splitLocaleFromPath(["pricing"], { defaultLocale: "fr", locales: ["fr", "de"] });
// -> { locale: "fr", path: ["pricing"] }Un premier segment ne compte comme locale que s'il figure dans locales et n'est pas la locale par défaut. Donc /fr/pricing n'est pas la page tarifs française : c'est une page dont le slug commence par hasard par fr. Une URL canonique par page et par langue, aucun doublon.
La route catch-all fait cela avant de chercher un slug :
const { path: strippedPath } = splitLocaleFromPath(path, await resolveSiteLocales());
const slug = "/" + (strippedPath ?? []).join("/");Traductions manquantes
Un champ retombe au lieu de disparaître :
pickLocalized(value, locale, defaultLocale);
// value[locale] ?? value[defaultLocale] ?? première valeur disponibleTrois étapes : la langue demandée, puis celle par défaut, puis ce qui existe. La dernière compte - un champ traduit uniquement en français s'affiche quand même sur une page allemande plutôt que de laisser un trou.
Conséquence pour les auteurs de blocs : une page partiellement traduite est un état normal, pas une erreur. Écrivez des composants qui s'affichent correctement quand un champ revient dans la mauvaise langue, et testez avec une locale que vous n'avez pas traduite - c'est la première que rencontreront vos visiteurs dans une nouvelle langue.
Middleware
export const proxy = createCmssyProxy(cmssy, {
// Seulement si vos URL portent la langue ET que vos routes sont des chemins
// statiques plutôt qu'une route catch-all.
stripLocalePrefix: true,
});Le SDK est explicite : laissez-le désactivé pour une application catch-all qui lit elle-même la langue dans le chemin. Activez-le quand même et la route reçoit un chemin sans préfixe : splitLocaleFromPath n'a plus rien à retirer et retombe sur la locale par défaut - toutes les langues s'affichent dans celle-ci.
Le middleware résout toujours la langue et la transmet dans un en-tête, une application peut donc la lire de là. Mais si votre route déduit la locale du chemin, comme le fait normalement une route catch-all, ces deux sources lui disent désormais des choses différentes.
Déclarer la langue dans le HTML
Définissez <html lang> à partir de la locale résolue. C'est ce que lisent les lecteurs d'écran et les outils de traduction, et c'est un contrat - d'où le fait que le test de fumée de l'éditeur s'y réfère plutôt que de chercher un mot dans vos textes. Les textes sont du contenu ; un éditeur peut les réécrire à tout moment, et alors le test ment.
Étapes suivantes
- SEO - les alternatives hreflang construites depuis le même ensemble de locales.
- Tests - l'assertion sur
<html lang>. - Modèles de données - les enregistrements sont multilingues aussi.