S'inscrire

Obtenez un contenu authentique à votre boîte mail

La internationalisation avec Astro

0:00
Log

J’adore l’internationalisation (i18n). C’est l’un de ces problèmes de domaine qui est suffisamment facile, bénéfique et satisfaisant pour être résolu tôt dans un projet. Il peut être très coûteux de le résoudre tard. Il a également des avantages latéraux peu considérés : j’ai vu des problèmes de pluralisation résolus comme count === 1 ? ‘apple’ : ‘apples’ trop souvent, et je vous dis que’il existe une manière mieux.

Pour ce site, j’ai pour objectif d’utiliser l’IA pour traduire le contenu dans autant de langues que le LLM le traitera correctement. C’est pour deux raisons : premièrement pour répondre à mon amour de l’i18n, et deuxièmement pour avoir une chance de travailler et m’assimiler plus avec l’IA et les LLM. Avec ce objectif en tête, nous pouvons attendre que des fichiers mdx anglaise sortiront dans des répertoires spécifiques pour chaque langue contenant les mêmes fichiers mdx mais traduits.

Traduire des fichiers mdx n’est pas le seul préoccupant cependant. Il reste encore d’autres éléments textuels généraux du site, par exemple comme noté lors de l’implémentation initiale le texte dans les étiquettes de catégorie (comme général/log/aventure).

Le support à l’internationalisation native d’Astro se concentre sur la détection de locale et la routage de locale : par exemple, vous pouvez placer du contenu anglais sous /en/, du contenu français sous /fr/ et ainsi de suite, en plus d’envoyer automatiquement l’utilisateur vers la route appropriée en fonction de leur locale de navigateur.

Cependant, un aspect manquant sur la page de documentation d’Astro sur l’internationalisation est comment créer des pages Astro qui importent et rendent du contenu spécifique à la langue à partir des répertoires de contenu spécifiques pour la langue courante. Il y a des miettes et des indices sur d’autres pages quant à cela, mais apportant tout ensemble les éléments clés sont les suivants.

Tout d’abord, nous devons veiller à déclarer les langues que Astro devrait supporter dans astro.config.mjs en ajoutant par exemple :

i18n: {
  defaultLocale: 'en',
  locales: ['en', 'fr'],
  routing: {
    prefixDefaultLocale: true,
  },
},

Ensuite, nous devons ajouter une page Astro à l’intérieur d’un répertoire lang comme /index.astro. Car ce chemin contient un paramètre de route, cette page index.astro devra exporter getStaticPaths (lors du mode SSG) pour indiquer au routeur toutes les chemins qui seront valides. Nous allons avoir besoin d’accès à la liste des langues fournie dans astro.config.mjs. Il convient de noter que cette liste est en réalité un abrégé des urls générées par les langues prises en charge. Par exemple, en-US et en-NZ mapperaient à en si configuré comme :

locales: [{ path: "en", codes: ["en-US", "en-NZ"] }];

Il n’y a pas de fonction Astro bâtie pour obtenir la liste des url-paths des langues directement, mais en utilisant getRelativeLocaleUrlList à partir d’astro:i18n, nous pouvons obtenir ce que nous avons besoin :

import { getRelativeLocaleUrlList } from "astro:i18n";

const pathSlugs = getRelativeLocaleUrlList().map((url) =>
  url.replaceAll("/", ""),
);

export function getStaticPaths() {
  return pathSlugs.map((locale) => ({
    params: { lang: locale },
  }));
}

Troisièmement, nous avions nos articles dans le répertoire contents/posts, mais maintenant nous devons les placer dans des répertoires spécifiques à la langue comme contents/posts/en et contents/posts/fr, etc. Alors que les collections d’Astro permettent des répertoires dans l’arbre comme ceci, les collections ne comprennent pas les langues, et nous devons donc mettre à jour le fichier index.astro une fois encore pour ne montrer que les articles appropriés à la langue courante. C’est malheureusement un peu manuel :

const currentLocalePosts = await getCollection(
  "posts",
  ({ id }) =>
    id.startsWith(
      `${getPathByLocale(Astro.currentLocale)}/`,
    ),
);

Il serait poli ici si Astro comprenait la relation entre les sous-répertoires de langues dans les collections et avait une vérification d’ensemble entre les codes de langue, les chemins, les noms de répertoire et le filtrage des collections. Malheureusement, comme cela se présente, nous devons gérer ces détails !

Nous avons résolu le premier élément sur notre liste pour afficher du contenu traduit à des URL appropriées, mais nous avons encore besoin d’internationaliser les aspects textuels du site.

Voilà que je recherche brièvement et ça ne semble pas y avoir de bibliothèque populaire évidente pour résoudre ce problème. Inspectant la documentation d’Astro, ils utilisent la bibliothèque Starlight qui gère cela manuellement sans aucune bibliothèque évidente. Le développement web basé sur le contenu est parfois un peu terre à terre.

Bien sûr, j’aimerais principalement utiliser la même API que react-intl, donc pour l’instant je décide juste de m’y prendre et d’implémenter un composant FormattedMessage basic, que j’ai l’habitude d’appeler M car c’est si fréquemment utilisé. J’ajoute un répertoire src/messages avec des fichiers en.json & fr.json à l’intérieur, et un fichier index qui exporte les json de chaque contenant paires de valeurs-clés. Puis mon fichier M.astro devient :

---
import { getPathByLocale } from "astro:i18n";
import msgs from "messages/index";
const messages = msgs as Record<
  string,
  Record<string, string>
>;
---

{
  messages[Astro.currentLocale][
    Astro.props.id
  ]
}

Si vous êtes perspicace, vous pourriez vous demander comment cela résout le premier problème de pluralisation, et vous avez raison - ce n’est pas ! Laissé ajouter la syntaxe de message FormatJS à notre composant M pour plus tard, mais il est probable qu’il s’agit juste de passer des valeurs à une fonction formatteuse.

Avec cela, nous avons les étiquettes traduites et en théorie le contenu traduit ! Sauf que nous n’avons pas encore réellement traduit le contenu. Pour cela, nous aurons besoin d’un pipeline de translation LLM qui vous pouvez lire dans le prochain article :).