si18n.js

v2.0.0

Une solution simple et légère pour intégrer l'internationalisation sur un projet de site web.

Journal des modifications (en)

Bonjour

Voici un exemple.

Cette phrase n'est pas disponible en anglais.

Il y a un singe sur l'arbre.
Il y a 12 singes sur l'arbre.


Langage actuelle : fr
Toutes les langues dispo. : fr, en

Démonstration du logo si18n.js

# Sommaire
Filtrer par package :
si18n.js Navigateur
Client navigateur complet. Traduction automatique des attributs HTML (data-si18n-*), gestion automatisée des basculeurs (boutons et sélecteurs <select>), basculement LTR/RTL, détection via URL ou navigateur, et persistance dans localStorage.
si18n.js/core Universel
Cœur de traduction agnostique et ultra-léger, sans dépendance DOM. Utilisable dans tout runtime JS (Navigateur, Node.js, Deno, Bun, React Native, Edge). Permet d'injecter vos propres dépendances et adaptateurs de stockage (StorageAdapter).
si18n.js/node Node.js
Implémentation optimisée pour Node.js. Chargement de fichiers locaux via node:fs/promises, détection automatique de la langue système (LC_ALL, LANG, etc.), et adaptateurs de stockage en mémoire ou dans process.env.
si18n.js/react React Client
Fournisseur de contexte <Si18nProvider> et hook useTranslation(). Rendu synchrone et sans scintillement grâce à useSyncExternalStore. Expose t, locale, locales, setLocale, isReady et error.
si18n.js/react/server React Server / RSC
Traducteur asynchrone createServerTranslator() pour les React Server Components (Next.js App Router), SSR et pipelines serveur. Zéro DOM et exécution ultra-rapide.
# Installation
npm
npm i si18n.js
yarn add si18n.js
CDN ES6 Modules
Vous pouvez utiliser si18n.js à partir d'un CDN. Différentes versions sont distribuées selon votre méthode d'import :
<!-- si18n.js latest version @2.0.0 -->
<script type="module" src="https://si18n.js.bruxelles.dev/si18n.js"></script>
<script type="module" src="https://si18n.js.bruxelles.dev/si18n.min.js"></script>

<!-- UNPKG -->
<script type="module" src="https://unpkg.com/si18n.js"></script>
<script type="module" src="https://unpkg.com/si18n.js@2.0.0/si18n.js"></script>
<script type="module" src="https://unpkg.com/si18n.js@2.0.0/si18n.min.js"></script>

<!-- jsDelivr -->
<script type="module" src="https://cdn.jsdelivr.net/npm/si18n.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/npm/si18n.js@2.0.0"></script>
<script type="module" src="https://cdn.jsdelivr.net/npm/si18n.js@2.0.0/si18n.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/npm/si18n.js@2.0.0/si18n.min.js"></script>

<!-- ESM -->
<!-- (?target=es2015|es2022|esnext) -->
<script type="module" src="https://esm.sh/si18n.js@2.0.0"></script>

<script type="module" src="https://cdn.jsdelivr.net/npm/si18n.js@2.0.0/+esm"></script>

<!-- jsDelivr -->
<script type="module">
  import Si18n from "https://cdn.jsdelivr.net/npm/si18n.js@2.0.0/si18n.min.js";
  // ...
</script>
CDN Classic Global Script
Script global classique (non module) qui expose la classe window.Si18n.
<!-- Load the global script (sets window.Si18n) -->
<script src="https://unpkg.com/si18n.js@2.0.0/si18n.es6.min.js"></script>
<script>
  const loc = new window.Si18n();
  // ...
</script>
ES6 Source versions
Modules source ES6 non transpilés. Recommandés si votre propre outil de bundling transpile les dépendances.
<!-- Unminified global script -->
<script src="https://unpkg.com/si18n.js@2.0.0/si18n.es6.js"></script>
# Utilisation
Navigateur (NPM / ESM)
import Si18n from "si18n.js";
// or import Si18nBrowser from "si18n.js/browser";

const loc = new Si18n();
await loc.init({
  path: "./locales",
  availableLocales: ["fr", "en"],
  fallbackLang: "fr",
  activeClass: "active",
  togglersSelector: ".lang-btn"
});
Core universel (Tout runtime JS sans DOM)
import { Si18nCore } from "si18n.js/core";

const loc = new Si18nCore();
await loc.init({
  locales: {
    en: { greeting: "Hello" },
    fr: { greeting: "Bonjour" }
  },
  lang: "fr",
  fallbackLang: "en"
});

console.log(loc.t("greeting")); // "Bonjour"
Node.js (CommonJS & ESM)
// CommonJS
const { Si18nNode, createMemoryStorage } = require("si18n.js/node");

// or ESM : import { Si18nNode } from "si18n.js/node";

const loc = new Si18nNode({
  path: "./locales",
  availableLocales: ["en", "fr"],
  fallbackLang: "en"
});

console.log(loc.t("greeting"));
TypeScript (Tous packages & Types)
// Targeted imports of classes and utilities
import Si18nBrowser, { type Si18nBrowserInitOptions } from "si18n.js/browser";
import { Si18nCore, type Si18nInitOptions, type StorageAdapter } from "si18n.js/core";
import { Si18nNode, createMemoryStorage, createProcessEnvStorage } from "si18n.js/node";
import { Si18nProvider, useTranslation, type TranslationContextValue } from "si18n.js/react";
import { createServerTranslator, type ServerTranslator } from "si18n.js/react/server";
React Client (<Si18nProvider> & useTranslation)
"use client";

import Si18nBrowser from "si18n.js/browser";
import { Si18nProvider, useTranslation } from "si18n.js/react";

const i18n = new Si18nBrowser();

function LanguagePicker() {
  const { t, locale, locales, setLocale, isReady, error } = useTranslation();

  if (error) return <p>Erreur : {error.message}</p>;
  if (!isReady) return <p>Chargement des traductions...</p>;

  return (
    <div>
      <h1>{t("welcome")}</h1>
      <select value={locale} onChange={(e) => void setLocale(e.target.value)}>
        {locales.map((lang) => (
          <option key={lang} value={lang}>{lang.toUpperCase()}</option>
        ))}
      </select>
    </div>
  );
}

export default function App() {
  return (
    <Si18nProvider
      i18n={i18n}
      options={{
        path: "./locales",
        availableLocales: ["fr", "en"],
        fallbackLang: "fr"
      }}
    >
      <LanguagePicker />
    </Si18nProvider>
  );
}
React Server Components (Next.js App Router / SSR)
// app/page.tsx (Server Component without DOM)
import { createServerTranslator } from "si18n.js/react/server";

export default async function Page() {
  const { t, locale } = await createServerTranslator({
    path: "./locales",
    availableLocales: ["fr", "en"],
    lang: "fr",
    fallbackLang: "fr"
  });

  return (
    <main>
      <h1>{t("welcome")}</h1>
      <p>Locale active sur le serveur : {locale}</p>
    </main>
  );
}
Exemple de base
let loc = new Si18n();
loc.init({
  locales: {}, // { "en": {...}, "fr": {...}}
  lang: "fr",
  fallbackLang: "en",
  translate() {
    document.querySelector(".lang").innerText = loc.getLocale();
    document.querySelector(".title").innerText = loc.t("nested.title");
    // ...
  },
  onLocaleChanged() {
    // ...
  }
});
Détection automatique & Attributs HTML
Dans le navigateur, si18n.js analyse et traduit automatiquement les balises portant les attributs suivants :
<div data-si18n="site.title"></div>
Attribut data-si18n-default
Lorsque data-si18n-default="false" est présent, le texte traduit n'est pas inséré comme contenu textuel de la balise. C'est indispensable lorsqu'on souhaite uniquement traduire des attributs (titre, placeholder, alt, etc.).
<div data-si18n="site.title" data-si18n-default="false" data-si18n-title="true"></div>
Liste complète des attributs supportés :

Les attributs suivants sont des drapeaux booléens ("true" ou "false") permettant de cibler des propriétés HTML spécifiques :

  • data-si18n : clé de traduction à appliquer (ex. "site.title").
  • data-si18n-default : si défini sur "false", n'écrase pas le contenu textuel de la balise.
  • data-si18n-html : si défini sur "true", injecte la traduction en tant que code HTML (innerHTML). Défaut : false.
  • data-si18n-vars : objet JSON de variables de remplacement transmis à t() (ex. data-si18n-vars='{"0": 12}').
  • data-si18n-placeholder : traduit l'attribut placeholder des champs <input> et <textarea>.
  • data-si18n-alt : traduit l'attribut alt des balises <img> pour l'accessibilité.
  • data-si18n-title : traduit l'attribut title de l'élément (infobulle native).
  • data-si18n-aria-label : traduit l'attribut aria-label pour les lecteurs d'écran.
  • data-si18n-label : traduit l'attribut label d'éléments comme <optgroup>.
  • data-si18n-value : traduit l'attribut value d'éléments comme <option> ou <input>.
  • data-si18n-content : traduit l'attribut content de balises comme <meta> (SEO / prerendering).
Les attributs lang et dir de la balise <html> sont définis automatiquement. Définissez simplement "rtl": true dans votre fichier de langue pour activer automatiquement le mode RTL. Par défaut : ltr.
Remplacement des valeurs
En plus des remplacements simples, si18n.js permet d'insérer des valeurs dynamiques dans vos chaînes grâce à la syntaxe %{nom} ou %{0}. Il suffit de transmettre un objet de clés-valeurs en second argument de t() :
<!-- 1. HTML -->
<p id="intro"></p>
// 2. JAVASCRIPT
// 2.1.
let data = {
  "fr": {
    "intro": "%{name} a eu %{age} ans cette année.",
    "test": "Ceci est un test."
  },
  "en": {
    "intro": "%{name} turned %{age} this year.",
    "test": "This is a test."
  }
};

loc.init({
  // ...
  translate() {
    document.querySelector("#intro").innerText = loc.t("intro", {
      age: (new Date().getFullYear()) - birthYear,
      name: "John Doe"
    });

    console.log(loc.t("test")) // "Ceci est un test."
    // ...
  }
});
ou
// 2.2.
let data = {
  "fr": { "intro": "Mon ami s'appelle %{0} et travaille chez %{1}." },
  "en": { "intro": "My friend's name is %{0} and he works at %{1}." }
};

loc.init({
  // ...
  translate() {
    document.querySelector("#intro").innerText = loc.t("intro", {
      0: "Georges",
      1: "CRFEV"
    });
    // ...
  }
});
# Plus de démos
Clés imbriquées (notation pointée)
Les clés peuvent être imbriquées à n'importe quelle profondeur et accédées avec la notation pointée :
const loc = new Si18n();

await loc.init({
  locales: {
    en: {
      site: {
        title: "My Website",
        nav: { home: "Home", about: "About" }
      }
    }
  },
  lang: "en",
  fallbackLang: "en"
});

console.log(loc.t("site.title"));    // "My Website"
console.log(loc.t("site.nav.home")); // "Home"
Chargement différé via path
Placez vos fichiers JSON de traduction dans un dossier et si18n.js les récupérera à la demande. Note : Seules la langue de secours et la langue active sont chargées au démarrage.
// locales/en.json, locales/fr.json, locales/de.json ...
const loc = new Si18n();

await loc.init({
  path: "./locales",
  availableLocales: ["en", "fr", "de"],
  lang: "en",
  fallbackLang: "fr" // loaded
});

console.log(loc.t("greeting")); // value from en.json
await loc.setLocale("fr");      // fetches fr.json on demand
console.log(loc.t("greeting")); // value from fr.json
Chargement différé via localeLoader
Utilisez une fonction asynchrone personnalisée — idéale pour le découpage de code (bundler) ou les chargeurs personnalisés :
const loc = new Si18n();

await loc.init({
  availableLocales: ["en", "fr"],
  lang: "en",
  fallbackLang: "en",
  localeLoader: async (locale) => {
    // Dynamic import — bundler will code-split automatically
    const module = await import(`./locales/${locale}.js`);
    return module.default;
  }
});
S'abonner aux changements de langue
subscribe() vous permet de réagir aux changements de langue n'importe où dans votre code :
// Subscribe — returns an unsubscribe function
const unsubscribe = loc.subscribe((newLocale) => {
  console.log("Locale changed to:", newLocale);
  document.title = loc.t("pageTitle");
});

await loc.setLocale("fr"); // triggers the listener

unsubscribe(); // stop listening
Support des langues RTL
Définissez rtl: true dans un objet de traduction. si18n.js définira automatiquement dir="rtl" sur l'élément <html> lorsque cette langue est active :
await loc.init({
  locales: {
    en: { greeting: "Hello" },
    ar: { rtl: true, greeting: "\u0645\u0631\u062d\u0628\u0627" }
  },
  lang: "en",
  fallbackLang: "en"
});

await loc.setLocale("ar");
// <html lang="ar" dir="rtl"> is set automatically
Callback onLocaleChanged
Exécutez des effets secondaires après un changement de langue (mettre à jour le titre de la page, rafraîchir du contenu dynamique, …) :
await loc.init({
  locales: {
    en: { pageTitle: "My App" },
    fr: { pageTitle: "Mon App" }
  },
  lang: "en",
  fallbackLang: "en",
  onLocaleChanged(newLocale, instance) {
    document.title = instance.t("pageTitle");
    console.log("Switched to:", newLocale);
  }
});
Node.js (Fichiers & Détection Système)
Utilisez la classe dédiée Si18nNode pour charger les fichiers locaux et détecter la langue système :
const { Si18nNode } = require("si18n.js/node");

const loc = new Si18nNode({
  path: "./locales",
  availableLocales: ["en", "fr"],
  fallbackLang: "en"
});

// Automatically detects system language (LC_ALL, LANG, etc.)
console.log(loc.getLocale());
console.log(loc.t("greeting"));
Node.js (Adaptateurs de Stockage)
Choisissez entre un stockage en mémoire volatile ou dans les variables d'environnement :
const { Si18nNode, createMemoryStorage, createProcessEnvStorage } = require("si18n.js/node");

// 1. In-memory volatile storage (default, does not pollute process.env)
const locMemory = new Si18nNode(
  { locales: { en: { hi: "Hi" } }, lang: "en" },
  { storage: createMemoryStorage() }
);

// 2. Persistence in process.env (e.g. process.env.APP_LANG)
const locEnv = new Si18nNode(
  { locales: { en: { hi: "Hi" } }, lang: "en", saveAs: "APP_LANG" },
  { storage: createProcessEnvStorage() }
);
React Client (<Si18nProvider> & useTranslation)
Exemple complet avec gestion de l'état de chargement (isReady), des erreurs et de la liste des langues :
import { Si18nProvider, useTranslation } from "si18n.js/react";
import Si18nBrowser from "si18n.js/browser";

const i18n = new Si18nBrowser();

function UserBadge() {
  const { t, locale, locales, setLocale } = useTranslation();
  return (
    <header>
      <span>{t("greeting", { name: "Alex" })}</span>
      {locales.map((code) => (
        <button
          key={code}
          disabled={code === locale}
          onClick={() => void setLocale(code)}
        >
          {code}
        </button>
      ))}
    </header>
  );
}
React Server Components (Next.js App Router / SSR)
Traduisez vos pages côté serveur de façon asynchrone avec createServerTranslator() :
// app/[lang]/page.tsx
import { createServerTranslator } from "si18n.js/react/server";

export default async function Page({ params }: { params: Promise<{ lang: string }> }) {
  const { lang } = await params;
  const { t, locale } = await createServerTranslator({
    path: "./locales",
    availableLocales: ["fr", "en"],
    lang,
    fallbackLang: "fr"
  });

  return (
    <section>
      <h1>{t("home.title")}</h1>
      <p>Langue rendue côté serveur : {locale}</p>
    </section>
  );
}
Core Universel & Adaptateur de Stockage personnalisé
Créez une instance avec un StorageAdapter sur mesure (AsyncStorage, cookie, Redis, etc.) :
import { Si18nCore, type StorageAdapter } from "si18n.js/core";

// Custom storage adapter (e.g. AsyncStorage, Redis, cookies...)
const customStorage: StorageAdapter = {
  getItem: (key) => window.sessionStorage.getItem(key),
  setItem: (key, val) => { window.sessionStorage.setItem(key, val); }
};

const loc = new Si18nCore(undefined, {
  storage: customStorage,
  detectLanguage: () => navigator.languages?.[0] ?? null
});

await loc.init({
  locales: { en: { ok: "OK" }, fr: { ok: "D'accord" } },
  lang: "fr"
});
# Méthodes & Fonctions (10)
Méthode / Fonction Argument Description
constructor options?:object,
dependencies?:object
Instancie l'objet si18n. Peut recevoir les options initiales et un objet de dependencies optionnel.
async init options:object Recommandé - Initialise l'objet si18n de façon asynchrone avec les options. Retourne une Promise<void>.
async setLocale lang:string Définit la langue active, charge la traduction si nécessaire et applique les changements. Retourne une Promise<void>.
getLocale void Renvoie le code de la langue actuelle (ex : 'fr').
getLocales void Renvoie la copie de la liste de toutes les langues disponibles (ex : ['fr', 'en']).
t JSONPath:string,
valeurs?:object
Renvoie la traduction à la clé donnée (supporte la notation pointée : site.nav.link). Accepte un objet de remplacement optionnel %{clé}.
subscribe listener:(locale) => void S'abonne aux changements de langue et renvoie une fonction de désabonnement () => void.
isInitialized void Renvoie true si l'instance est initialisée, sinon false.
static async getJSON urlOrPath: string,
callback?(res: object): fn
Méthode statique asynchrone récupérant un JSON depuis une URL (navigateur via fetch) ou un chemin de fichier (Node.js via fs). Retourne Promise<T> ou passe le résultat au callback optionnel.
toJSON void Renvoie un instantané sérialisé des options de configuration de l'instance.
createMemoryStorage void Crée un adaptateur de stockage en mémoire volatile (StorageAdapter) basé sur une Map.
createProcessEnvStorage void Crée un adaptateur de stockage persistant dans process.env[saveAs].
async createServerTranslator options:object,
instance?:Si18nLike
Fonction asynchrone créant un traducteur { t, locale, locales, i18n } pour les composants serveur (RSC / Next.js / SSR).
<Si18nProvider> props:{ i18n, options, children } Composant <Si18nProvider> synchronisant l'instance avec l'arbre React sans scintillement via useSyncExternalStore.
useTranslation void Hook React retournant { t, locale, locales, setLocale, isReady, error, i18n }.
# Options par défaut (15)
Argument Valeur par défaut Description
locales {} Objet contenant vos traductions des langues. Obligatoire seulement si path ou localeLoader n'est pas définie.
path null Chemin d'accès vers le dossier contenant vos fichiers JSON de traduction. Les fichiers vont être chargés automatiquement selon la langue active (la langue de secours sera toujours chargée). Prioritaire sur l'option locales. Obligatoire seulement si locales n'est pas définie. Ex : ./locales.
localeLoader undefined Fonction asynchrone personnalisée (locale) => Promise<object> pour charger dynamiquement les traductions. Idéale pour le découpage de code (bundlers) ou les chargeurs personnalisés.
availableLocales [] Liste contenant les noms des langues disponibles tel que défini dans les fichiers de traduction. Obligatoire seulement si path est définie.
lang '' Langue active à utiliser. Si omise, si18n.js détecte la langue depuis l'URL, le stockage ou l'environnement (navigateur / variables système). Si cette option n'est pas définie, l'option fallbackLang devient obligatoire.
fallbackLang '' Langue de secours. La langue à utiliser lorsque la langue demandée ou une clé dans une langue incomplète est manquante. Si elle n'est pas définie, la valeur de lang sera utilisée.
activeClass '' Classe CSS à ajouter au bouton basculeur actif pour la langue actuelle. À utiliser avec togglersSelector.
togglersSelector '' Sélecteur CSS pour les éléments basculeurs. si18n.js attache automatiquement les écouteurs d'événements. Ces éléments doivent obligatoirement posséder l'attribut data-lang="XX".
isTogglerSelect false Définir à true si le sélecteur correspond à un élément <select>. Les options non disponibles sont automatiquement désactivées.
sanitizeHtml undefined Fonction optionnelle (html: string) => string appelée pour assainir le HTML avant son injection lorsque data-si18n-html="true" est utilisé (ex : DOMPurify.sanitize).
saveLang true S'il faut enregistrer la langue sélectionnée dans le stockage persistant (localStorage pour le navigateur, mémoire ou process.env pour Node.js).
saveAs 'lang' Nom de la clé de stockage persistant et du paramètre URL (Défaut : 'lang').
reloadPage false Si cette option est définie à true, la page sera automatiquement actualisée après avoir changé la langue.
translate function() {} Fonction de callback appelée pour appliquer les traductions manuelles.
Note : Pour le navigateur, la balise <html> reçoit automatiquement les attributs lang et dir.
onLocaleChanged function() {} Callback central déclenché lors d'un changement de langue (à init et setLocale). Reçoit (locale, instance).
dependencies {} Objet d'injection de dépendances pour Si18nCore et Si18nNode : storage (adaptateur StorageAdapter), detectLanguage, getLanguageFromUrl, createPathLocaleLoader, reload, logError.
By José
dBruxelles