Fermer

février 22, 2023

Obtenir l’internationalisation (i18n) avec Remix et Headless CMS

Obtenir l’internationalisation (i18n) avec Remix et Headless CMS


Quelle est l’importance de la barrière de la langue au 21e siècle ? Vous, en tant que lecteur, êtes probablement très familier avec l’anglais, mais qu’en est-il des autres ?

De nos jours, la plupart d’entre nous ont souvent entendu parler de l’importance de l’accessibilité, de meilleures performances et d’une meilleure UX ou DX. Il se peut que vous n’entendiez pas ou que vous ne voyiez pas souvent parler d’i18n par rapport à ces sujets. Mais si vous voyez des faits et des chiffres dans les statistiques, vous pourriez trouver des résultats surprenants sur i18n et son impact. Découvrons cela ensemble.

i18n et l10n

Avant de passer en revue l’impact d’i18n, apprenons la différence entre les deux terminologies.

  • i18n
    i18n signifie internationalisation. Entre le premier caractère « i » et le dernier caractère « n » de ce mot, il y a 18 caractères. i18n décrit la mise en œuvre des structures et des fonctionnalités pour que vos applications soient prêtes à localiser le contenu.
  • l10n
    l10n signifie localisation. Entre le premier caractère « l » et le dernier caractère « n » de ce mot, il y a dix caractères. l10n signifie traduire le contenu dans les langues pour les utilisateurs qui accèdent à partir de régions spécifiques.

En guise de suivi, i18n contient un processus de programmation pour implémenter des fonctionnalités permettant aux éditeurs de contenu et aux traducteurs de démarrer le processus l10n à partir de l’interface utilisateur.

Pourquoi i18n est-il si important ?

Pour voir l’importance d’i18n, regardons les chiffres et les statistiques pour obtenir des informations objectives. Vous verrez les chiffres de certains faits ci-dessous, et avant de lire plus loin, devinons ce que ces chiffres représentent.

  • 5,07 milliards
  • 25,9 %
  • 74,1 %
  • Chine
  • Asie

Le premier fait montre une quantité énorme de chiffres. 5,07 milliards, c’est le nombre d’utilisateurs dans ce monde en 2020. La population mondiale en 2021 était de 7,837 milliards, soit près de 8 milliards. Plus de la moitié de la population mondiale a accès à du contenu sur Internet et à des applications.

Sur la base du nombre d’utilisateurs dans ce monde, il existe une autre recherche sur les langues les plus couramment utilisées sur Internet. En regardant le graphique, la plupart d’entre nous prêtent attention au nombre le plus élevé sur ce graphique : 25,9 %, anglais.

Langues les plus couramment utilisées sur Internet en janvier 2020, par part des internautes
(Source des images : Homme d’État) (Grand aperçu)

Le deuxième plus élevé est le reste des langues, 23,1%. Supposons également que vous rassembliez le reste du pourcentage, à l’exception de l’anglais. Dans ce cas, vous pouvez vous rendre compte que sur plus de 5 milliards d’utilisateurs, 74,1% des utilisateurs accèdent au contenu dans n’importe quelle autre langue.

Après avoir passé en revue ces faits, nous pouvons maintenant expliquer pourquoi l’internationalisation et la localisation de votre contenu pour l’Asie et la Chine, en particulier, sont cruciales. La Chine compte le plus d’internautes dans le monde. En conséquence, plus de la moitié de tous les internautes dans le monde viennent d’Asie.

D’après ce que nous avons vu, nous ne pouvons probablement pas ignorer la localisation du contenu. Cela améliorera l’expérience utilisateur si ces énormes quantités d’utilisateurs dans le monde pouvaient avoir un contenu localisé. Après avoir connu l’impact potentiel d’un bon i18n, regardons la logique fondamentale.

Comment i18n fonctionne à un niveau de base

Quelle que soit la technologie utilisée pour mettre en œuvre i18n, il existe trois façons de déterminer les langues et les régions.

  1. L’emplacement de l’adresse IP
  2. Accepter-Langue entête/Navigateur.langues
  3. Identifiants dans les URL

L’utilisation de l’adresse IP détecte la région des utilisateurs et leur permet d’accéder au contenu dans leurs langues régionales. Cependant, l’adresse IP des utilisateurs ne correspond pas nécessairement à leur préférence linguistique. De plus, L’analyse de localisation empêche les sites d’être explorés par les moteurs de recherche.

En utilisant Accepter-Langue en-tête ou Navigateur.langues est une autre approche possible pour implémenter i18n. Cependant, cette approche fournit des informations linguistiques mais pas des informations régionales.

i18n ne consiste pas seulement à localiser du contenu. Cela inclut également l’amélioration de l’UX. Par exemple, la création d’identifiants dans les URL améliore l’UX. Cela aide également à diviser le contenu localisé dans le système dédié. Nous verrons comment il est possible de mettre en œuvre un tel système dans le « Une combinaison de remix et de CMS » section.

En règle générale, les identifiants dans les URL existent dans trois modèles différents :

  1. Différencier par domaines (c’est-à-dire hello.es, hello.jp)
  2. Paramètres d’URL (c’est-à-dire hello.com?loc=de)
  3. Sous-répertoires localisés (c’est-à-dire hello.com/es, hello.com/ja)

Pour suivre la politique de même origine pour un meilleur référencement, des sous-répertoires localisés peuvent être utilisés.

Sur la base des faits intéressants et de la logique fondamentale pour implémenter i18n, nous parlerons des frameworks et des bibliothèques, car certains d’entre eux utilisent des bibliothèques i18n.

Bibliothèques

Afin de ne pas réinventer la roue chaque fois que nous voulons implémenter i18n dans nos projets, les développeurs disposent de différentes bibliothèques, outils et services qui peuvent être utilisés pour faciliter le travail. Si nous travaillons avec des frameworks React ou basés sur React, nous avons différentes options disponibles. Parlons de certains d’entre eux.

Format.js

Format.js est une collection modulaire de bibliothèques JavaScript que nous pouvons utiliser pour implémenter la logique i18n à la fois sur le client et sur le serveur. Ce groupe de bibliothèques se concentre sur le formatage des nombres, des dates et des chaînes. Il offre différentes fonctionnalités et outils et s’exécute dans le navigateur ainsi que dans l’environnement d’exécution Node.js. Il s’intègre à divers frameworks, comme Vue et React, afin que nous puissions utiliser ses fonctionnalités sur nos projets Remix. Vous pouvez en savoir plus sur le docs officiels de React Intl.

i18suivant

Une autre alternative que nous pouvons évaluer pour notre projet est i18suivant. Cette bibliothèque JavaScript va au-delà des fonctionnalités standard i18n, fournissant une suite complète pour gérer i18n dans nos projets. Nous pouvons détecter la langue des utilisateurs, mettre en cache les traductions et même installer des plugins et des extensions. Comme il a été construit en JavaScript, nous pouvons utiliser cet outil pour les sites Web ainsi que pour les applications mobiles et de bureau.

Qu’en est-il du Remix ?

Lors de la création d’un site Web à l’aide de Remix, nous avons différentes options à considérer. Comme il s’agit d’un framework basé sur React, nous pouvons utiliser n’importe laquelle des bibliothèques mentionnées précédemment. Cependant, nous passerons en revue deux approches qui peuvent mieux s’adapter à vos projets Remix. Tout d’abord, nous verrons comment localiser le contenu à l’aide de remix-i18next, une bibliothèque spécifique à Remix pour i18n. Deuxièmement, nous utiliserons un système de gestion de contenu sans tête comme source des différentes langues/locales de notre contenu.

remix-i18next

Basé sur i18next, Sergio Xalambril’un des principaux contributeurs de Remix, a créé remix-i18next. Cette bibliothèque offre des fonctionnalités et des modules similaires à la bibliothèque JavaScript, mais en se concentrant sur les concepts et les approches Remix. Facile à configurer et à utiliser, prêt pour la production et sans aucune exigence ni dépendance. Voyons de plus près comment implémenter i18n dans nos projets Remix en utilisant remix-i18next.

Tout d’abord, nous devons installer des packages npm :

npm install remix-i18next i18next react-i18next i18next-browser-languagedetector i18next-http-backend i18next-fs-backend

Tous nous aideront à gérer i18n à la fois côté serveur et côté client de notre site Web. Nous les utiliserons également pour configurer notre backend et définir la logique qui détectera la langue de l’utilisateur.

Maintenant, nous devons ajouter une configuration qui sera utilisée sur le site Web à la fois du côté client et du côté serveur. Créons quelques fichiers JSON avec les traductions des différentes chaînes de caractères que nous utiliserons sur notre site Web :

{
  "intro": "Hello everyone!"
}
{
  "intro": "Hola a todos!"
}

En nommant les fichiers « common.json », nous définissons l’espace de noms pour les chaînes ce nous les énumérerons.

Maintenant, créons un fichier appelé i18n.js. Ce fichier contient différents paramètres de configuration que nous utiliserons au moment de l’initialisation de notre serveur i18n.

export default {
  supportedLngs: ["en", "es"],
  fallbackLng: "en",
  defaultNS: "common",
  // Disabling suspense is recommended
  react: { useSuspense: false },
};

Vous pouvez voir plus d’options de configuration dans le documents officiels i18next.

Maintenant, créez le fichier i18next.server.jsqui contient la logique qui sera utilisée dans le entry.server.jsx fichier de notre projet Remix.

import Backend from "i18next-fs-backend";
    import { resolve } from "node:path";
    import { RemixI18Next } from "remix-i18next";
    import i18n from "~/i18n"; // The configuration file we created
    
    let i18next = new RemixI18Next({
      detection: {
        supportedLanguages: i18n.supportedLngs,
        fallbackLanguage: i18n.fallbackLng,
      },
      i18next: {
        ...i18n,
        backend: {
          loadPath: resolve('./public/locales/{{lng}}/{{ns}}.json'),
        },
      },
      backend: Backend,
    });
    
    export default i18next;

Nous initialisons essentiellement un nouveau serveur i18n qui fonctionnera avec notre backend Remix. Nous spécifions l’emplacement des fichiers JSON contenant les traductions à utiliser. Ajoutons ces fonctionnalités à nos principaux fichiers de configuration Remix. Tout d’abord, nous ajoutons une logique pour pouvoir traduire le contenu côté client. Pour ce faire, éditons notre fichier `entry.client.jsx` :

import i18next from "i18next";
import LanguageDetector from "i18next-browser-languagedetector";
import Backend from "i18next-http-backend";
import { I18nextProvider, initReactI18next } from "react-i18next";
import { getInitialNamespaces } from "remix-i18next";
import i18n from "./i18n"; // The configuration file we created

i18next
  .use(initReactI18next)
  .use(LanguageDetector)
  .use(Backend)
  .init({
    ...i18n, // The same config we created for the server
    ns: getInitialNamespaces(),
    backend: {
      loadPath: "/locales/{{lng}}/{{ns}}.json",
    },
    detection: {
      order: ["htmlTag"],
      caches: [],
    },
  })
  .then(() => {
    // After i18next init, hydrate the app
    hydrateRoot(
      document,
      // Wrap RemixBrowser in I18nextProvider
      <I18nextProvider i18n={i18next}>
        <RemixBrowser />
      </I18nextProvider>
    );
  });

Nous devons attendre pour nous assurer que les traductions sont chargées avant l’hydratation afin de garder notre application Web interactive.

Ajoutons la logique au entry.server.jsx déposer maintenant :

import { createInstance } from "i18next";
import Backend from "i18next-fs-backend";
import { resolve } from "node:path";
import { I18nextProvider, initReactI18next } from "react-i18next";
import i18next from "./i18next.server"; // The backend file we created
import i18n from "./i18n"; // The configuration file we created

...

export default async function handleRequest(
...
) {
  // We create a new instance of i18next
  let instance = createInstance();

  // We can detect the specific locale from each request
  let lng = await i18next.getLocale(request);
  // The namespaces the routes about to render wants to use
  let ns = i18next.getRouteNamespaces(remixContext);

  await instance
    .use(initReactI18next)
    .use(Backend)
    .init({
      ...i18n,// The config we created
      lng, // The locale we detected from the request
      ns,
      backend: {
        loadPath: resolve("./public/locales/{{lng}}/{{ns}}.json"),
      },
    });

  return new Promise((resolve, reject) => {
    ...

    let { pipe, abort } = renderToPipeableStream(
      
        {" "}
      ,
      ...
    );
    ...
  });
}

L’identification de la langue préférée des utilisateurs nous permettra, entre autres, de les rediriger vers certaines routes.

Nous pouvons maintenant commencer à utiliser les fonctionnalités fournies par remix-i18next pour détecter les paramètres régionaux de l’utilisateur et fournir un contenu traduit en fonction de cela. Modifions le root.jsx déposer:

...

import { json } from "@remix-run/node";
import { useChangeLanguage } from "remix-i18next";
import { useTranslation } from "react-i18next";
import i18next from "~/i18next.server";

...

export let loader = async ({ request }) => {
  let locale = await i18next.getLocale(request);
  return json({ locale });
};

export let handle = {
  i18n: "common",
};

export default function App() {
  // Get the locale from the loader
  let { locale } = useLoaderData();
  let { i18n } = useTranslation();

  useChangeLanguage(locale);

  return (
    <html lang={locale} dir={i18n.dir()}>
      ...
    </html>
  );
}

Le useChangeLanguage hook changera la langue de l’instance en la locale détectée par le chargeur. Chaque fois que nous faisons quelque chose pour changer la langue, cette locale change et i18next charge les traductions correctes.

Désormais, nous sommes en mesure de traduire du contenu dans n’importe quel itinéraire :

import { useTranslation } from "react-i18next";

export default function MyPage() {
  let { t } = useTranslation();
  return <h1>{t("intro")}</h1>;
}

Nous utilisons le t() pour afficher les chaînes traduites en fonction de la liste des messages que nous avons définie dans nos fichiers JSON.

Dans cet exemple, nous utilisons un espace de noms par défaut, mais nous pouvons configurer plusieurs espaces de noms si nous le souhaitons. Vous pouvez en savoir plus sur le t() fonction dans les documents officiels i18next.

Dans le cas où nous voulons traduire le contenu côté serveur, nous pouvons utiliser le getFixedT méthode à l’intérieur de nos chargeurs et actions :

import i18next from "~/i18next.server";

...

export let loader = async ({ request }) => {
  let t = await i18next.getFixedT(request);
  let title = t("intro");
  return json({ title });
};

Une combinaison de remix et de CMS

Ensemble, nous avons exploré les options disponibles pour implémenter i18n avec Remix. Au début de cet article, nous avons appris que i18n pourrait entraîner une amélioration considérable de l’UX, du SEOUX et du SEO. Dans le cadre de l’UX, il est également important d’inclure une meilleure DX.

L’approche ci-dessus crée des fichiers de traduction au niveau du code source. De plus, nous n’avons pas la logique pour implémenter des identifiants dans les URL. Pour y parvenir, intéressons-nous à la démarche d’intégration d’un CMS. Dans cet article, nous utiliserons Storyblok, qui propose trois approches différentes pour localiser le contenu et les poignées pour déterminer les langues et les régions.

Note: Si vous souhaitez créer la connexion entre votre application Remix et Storyblok, il y a un tutoriel de 5 minutes cela explique juste comment faire cela.

Après cela, vous pouvez rapidement cloner un espace de démarrage en utilisant ce lien magique pour disposer de tous les composants et types de champs nécessaires. Cet espace d’exemple couvre une approche appelée traduction au niveau du dossier. Nous verrons de quoi il s’agit dans la section suivante.
https://app.storyblok.com/#!/build/181387

Choisissez entre trois approches

Storyblok propose trois façons de créer la mise en page pour stocker le contenu localisé et déterminer les langues et les régions.

  1. Traduction au niveau du dossier: Divisez le contenu localisé au niveau du dossier.
  2. Traduction au niveau du champ: Traduire au niveau du type de champ.
  3. Traduction au niveau de l’espace: Dédiez des espaces (environnements ou référentiels) à certains contenus localisés.

Pour couvrir les identifiants dans les URL, la traduction au niveau du dossier fonctionne parfaitement, car chaque dossier ne contiendra que du contenu localisé pertinent.

Traduction au niveau du dossier
(Grand aperçu)

De plus, les identifiants peuvent être modifiés à partir des paramètres du dossier via le slug.

Paramètres du dossier
(Grand aperçu)

En modifiant le slug à partir de l’écran des paramètres du dossier, cet identifiant localisé dans l’URL apparaît dans toutes les histoires à l’intérieur de ce dossier japonais. Par exemple, la page à propos du dossier japonais possède déjà un identifiant localisé dans l’URL.

Teaser général de la page Storyblok
(Grand aperçu)

Pour générer par programmation des pages de contenu, Remix dispose d’une fonctionnalité appelée Lieu, attrapant tous les slugs quels que soient les niveaux imbriqués. Nommer un fichier $.jsx activera la fonction fondamentale du slug fourre-tout.

app
├── root.jsx
└── routes
    ├── files
    │   └── $.jsx
    └── files.jsx

La différence entre segments dynamiques de Remix est que les flocs correspondent toujours au prochain /. Par conséquent, les splats captureront tout sur le chemin. Si le chemin de l’URL est hello.com/ja/about/somethingla route splat a un paramètre spécial pour capturer les segments de fin de l’URL.

export async function loader({ params }) {
  params["*"]; // "ja/about/something"
}

En utilisant le paramètre spécial de la route splat, éditons $.jsx déposer.

export default function Page() {
 // useLoaderData returns JSON parsed data from loader func
 let story = useLoaderData();
 story = useStoryblokState(story, {
   resolveRelations: ["featured-posts.posts", "selected-posts.posts"]
 });
 return <StoryblokComponent blok={story.content} />
};
// loader is Backend API & Wired up through useLoaderData
export const loader = async ({ params, preview = false }) => {
 let slug = params["*"] ?? "home";
 slug = slug.endsWith("/") ? slug.slice(0, -1) : slug;
 let sbParams = {
   version: "draft",
   resolve_relations: ["featured-posts.posts", "selected-posts.posts"],
 };
 // …
 let { data } = await getStoryblokApi().get(`cdn/stories/${slug}`,
 sbParams);
 return json(data?.story, preview);
};

INDICE: Dans la section « Choisir entre 3 approches », nous n’avons pas couvert les trois approches, mais si vous souhaitez en savoir plus, toutes les approches sont documentées ci-dessous.

Résumé

Nous avons appris ensemble les faits et les statistiques pour connaître les impacts et l’importance de l’i18n et avons vu comment Remix gère plusieurs options pour mettre en œuvre l’i18n avancé. Fait intéressant, une meilleure expérience i18n offre un meilleur référencement et UX. J’espère que cet article vous a fourni de nouvelles connaissances et des apprentissages perspicaces.

Éditorial fracassant
(vf, il)




Source link