Skip to content

Latest commit

 

History

History
154 lines (127 loc) · 5.71 KB

i18n.md

File metadata and controls

154 lines (127 loc) · 5.71 KB

i18n - internationalisation

Côté back

A compléter

Côté front

L'internationalisation du site cartes.gouv.fr est réalisée avec la librairie i18nifty.

Les chaînes sont habituellement regroupées par un composant, ou parfois par un groupe de composants d'un même contexte. Elles sont stockées dans un fichier dédié à côté du ou des fichiers où elles sont utilisées. Elles sont ainsi en de très nombreux endroits du code source mais au plus proche de leur utilisation réelle.

Important

Convention de nommage : [nom du composant].locale.tsx

Voici un exemple d'utilisation avec le composant Contact (pages/contact/Contact.tsx) :

  1. Créer un fichier Contact.locale.tsx dans pages/contact

  2. Déclarer toutes les clés des textes dans ce fichier :

// Contact.tsx
import { JSX } from "react";
import { declareComponentKeys } from "../i18n/i18n";

const { i18n } = declareComponentKeys<
    | "title"
    | "mandatory_fields"
    | "form.error_title"
    | { K: "form.explanation"; P: { docsLinkProps: RegisteredLinkProps }; R: JSX.Element }
    | "form.email_contact"
    | "form.email_contact_hint"
    | "form.email_contact_mandatory_error"
    | "documentation"
    | { K: "warning"; R: ReactNode }
    | { K: "field_required"; P: { name: string } }
>()("Contact");
export type I18n = typeof i18n;

Note

  • K : la clé (obligatoire)
  • P : le type des paramètres de la fonction (optionnel, undefined par défaut)
  • R : le type de la valeur retournée par la fonction (optionnel, string par défaut)

Pour un texte simple, il suffit de déclarer la clé, par exemple "mandatory_fields" ou "title".

Pour retourner une chaîne mais avec des interpolations, il suffit de déclarer la clé et le type des paramètres, par exemple "field_required".

Pour retourner autre chose que du texte mais sans forcément avoir de paramètres, il suffit de déclarer la clé et le type de retour, par exemple "warning".

Le cas maxi, pour retourner autre chose que du texte avec des interpolations, il faut déclarer les trois K, P et R, par exemple "form.explanation"

  1. Ajouter le type retourné I18n de declareComponentKeys à ComponentKey dans le fichier i18n/types.ts (qui est une union de toutes les déclaration de clés) :
// i18n/types.ts
export type ComponentKey =
    | import("../pages/contact/Contact").I18n
    | import("../config/navItems").I18n
    | ...
  1. Ajouter les textes traduits pour chaque langue (dans le fichier .locale.tsx)
// Contact.locale.tsx
import type { Translations } from "../../../i18n/types";

export const ContactFrTranslations: Translations<"fr">["Contact"] = {
    title: "Nous écrire",
    mandatory_fields: "Sauf mention contraire “(optionnel)” dans le label, tous les champs sont obligatoires.",
    "form.error_title": "Votre message n'a pas pu être envoyé",
    "form.explanation": ({ docsLinkProps }) => (
        <>
            {"Vous n'avez pas trouvé la réponse à votre question dans "}
            <a {...docsLinkProps}>{"l'aide en ligne"}</a>
            {" ? Vous souhaitez la configuration d'un espace de travail pour vos besoins ? Utilisez ce formulaire pour nous contacter."}
        </>
    ),
    "form.email_contact": "Votre email",
    "form.email_contact_hint": "Format attendu : [email protected]",
    "form.email_contact_mandatory_error": "Veuillez saisir une adresse email",
    documentation: "Documentation",
    warning: <Alert severity="error" title="Une erreur est survenue" className={fr.cx("fr-my-3w")} />,
    field_required: ({ name }) => `Champ ${name} est obligatoire`,
};

export const ContactEnTranslations: Translations<"en">["Contact"] = {
    title: "Contact us",
    mandatory_fields: "All fields are mandatory unless label states “optional”",
    "form.error_title": "Your message could not be sent",
    "form.explanation": ({ docsLinkProps }) => (
        <>
            {"You did not find the answer to your question in "}
            <a {...docsLinkProps}>{"our documentation"}</a>
            {"? Do you want to configure a workspace for your needs? Use this form to contact us."}
        </>
    ),
    "form.email_contact": "Email",
    "form.email_contact_hint": "Expected format: [email protected]",
    "form.email_contact_mandatory_error": "Enter an email address",
    documentation: undefined,
    warning: undefined,
    field_required: undefined,
};

Important

Garder une homogénéité des noms de ces variables : [nom du composant][langue]Translations

Note

Même si tous les textes ne sont pas traduits dans chaque langue, il faut quand-même déclarer la clé pour toutes les langues en la mettant à undefined, comme c'est le cas de "documentation"

  1. Importer et ajouter les textes de traduction de chaque composant dans les fichiers d'agrégation par langue :
// i18n/languages/fr.tsx et i18n/languages/en.tsx
import type { Translations } from "../i18n/types";
import { navItemsFrTranslations } from "../../config/navItems";
import { ContactFrTranslations } from "../../pages/contact/Contact";

export const translations: Translations<"fr"> = {
    Contact: ContactFrTranslations,
    navItems: navItemsFrTranslations,
};
  1. Utiliser les textes de traduction :
// Contact.tsx
import { useTranslation } from "../i18n/i18n";
const Contact = () => {
    const { t } = useTranslation({ Contact });

    ...

    return (
        <AppLayout documentTitle={t("title")}>
            <div className={fr.cx("fr-grid-row")}>
                <div className={fr.cx("fr-col-12", "fr-col-md-8")}>
                    <h1>{t("title")}</h1>
                    ...
                </div>
            </div>
        </AppLayout>
    );
};