Good morning
This is an example.
Cette phrase n'est pas disponible en anglais.
There is one monkey in the tree.
There are 12 monkeys in the tree.
Current language : en
All available languages : fr, en
# Table of content
Filter by package:
si18n.js
Browser
Full browser client. Automatic HTML attribute translations (
data-si18n-*), automated toggler buttons and <select> pickers, LTR/RTL switching, URL & browser language detection, and localStorage persistence.si18n.js/core
Universal
Platform-agnostic and ultra-lightweight translation core with zero DOM dependencies. Runs in any JS runtime (Browser, Node.js, Deno, Bun, React Native, Edge). Pluggable dependencies and custom storage adapters (
StorageAdapter).si18n.js/node
Node.js
Optimized Node.js implementation. Local filesystem file loading via
node:fs/promises, automatic OS language detection (LC_ALL, LANG, etc.), and built-in memory or process.env storage adapters.si18n.js/react
React Client
Context provider
<Si18nProvider> and useTranslation() hook. Synchronous, tear-free rendering powered by useSyncExternalStore. Exposes t, locale, locales, setLocale, isReady and error.si18n.js/react/server
React Server / RSC
Async
createServerTranslator() translator for React Server Components (Next.js App Router), SSR and server rendering pipelines. Zero DOM and lightning-fast execution.# Installation
npm i si18n.js
yarn add si18n.js
CDN ES6 Modules
You can also use si18n.js from a CDN. Different builds are distributed depending on your import style:
<!-- 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
Classic global script (non-module) which sets the global class
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
Untranspiled ES6 source modules. Recommended if your own bundler handles transpiling node_modules.
<!-- Unminified global script -->
<script src="https://unpkg.com/si18n.js@2.0.0/si18n.es6.js"></script>
# Usage
Browser (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"
});
Universal Core (Any JS runtime without 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 (All 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>
);
}
Basic example
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() {
// ...
}
});
Automatic detection & HTML attributes
In browser environments, si18n.js automatically inspects and translates elements bearing these attributes:
<div data-si18n="site.title"></div>
data-si18n-default attribute
When
data-si18n-default="false" is set, the translation is not inserted into the element's textContent. Useful when only translating HTML attributes (title, placeholder, alt, etc.).<div data-si18n="site.title" data-si18n-default="false" data-si18n-title="true"></div>
Complete list of supported attributes:
The following attributes are boolean flags ("true" or "false") targeting specific element properties:
data-si18n: translation key to apply (e.g."site.title").data-si18n-default: if set to"false", does not overwrite the element's text content.data-si18n-html: if set to"true", injects the translated string as HTML (innerHTML). Default:false.data-si18n-vars: JSON object of replacement variables passed tot()(e.g.data-si18n-vars='{"0": 12}').data-si18n-placeholder: translates theplaceholderattribute of<input>and<textarea>elements.data-si18n-alt: translates thealtattribute of<img>elements for accessibility.data-si18n-title: translates the element's native tooltiptitleattribute.data-si18n-aria-label: translates thearia-labelattribute for screen readers.data-si18n-label: translates thelabelattribute of tags like<optgroup>.data-si18n-value: translates thevalueattribute of tags like<option>or<input>.data-si18n-content: translates thecontentattribute of tags like<meta>(SEO / prerendering).
The
lang and dir attributes of the <html> tag are set automatically. Set "rtl": true in your locale file to activate RTL direction automatically. Default: ltr.
Replacement of values
Besides simple text replacements, si18n.js can insert dynamic values into placeholders with
%{name} or %{0} syntax. Simply pass a key-value object as the second argument to 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."
// ...
}
});
or
// 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"
});
// ...
}
});
# More demos
Nested keys (dot-notation)
Keys can be nested to any depth and accessed with dot-notation:
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"
Lazy-loading via
path
Place your locale JSON files in a directory and si18n.js will fetch them on demand. Note: Only the fallback and active locales are loaded at startup.
// 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
Lazy-loading via
localeLoader
Use a custom async function — ideal for bundler code-splitting or custom server loaders:
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;
}
});
Subscribing to locale changes
subscribe() lets you react to locale changes anywhere in your 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
RTL language support
Set
rtl: true inside a locale object. si18n.js will automatically set dir="rtl" on the <html> element when that locale is 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
onLocaleChanged callback
Run side-effects after a locale switch (update page title, refresh dynamic content, …):
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 (Filesystem & System Detection)
Use the dedicated
Si18nNode class to load local files and detect system locale: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 (Storage Adapters)
Choose between volatile in-memory storage or environment variable persistence:
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)
Production-ready example handling ready state (
isReady), errors, and available locales: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)
Translate pages server-side asynchronously without a DOM using
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>
);
}
Universal Core & Custom Storage Adapter
Create an instance with a custom
StorageAdapter (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"
});
# Methods & Functions (10)
| Method / Function | Argument | Description |
|---|---|---|
constructor |
options?:object,dependencies?:object |
Instantiates the si18n object. Accepts initial options and an optional dependencies object. |
async init |
options:object |
Recommended - Asynchronously initializes the si18n object with given options. Returns a Promise<void>. |
async setLocale |
lang:string |
Sets the active language, loads translation data if needed, and applies changes. Returns a Promise<void>. |
getLocale |
void |
Returns the current language code (e.g.: 'en'). |
getLocales |
void |
Returns a copy of all available languages (e.g.: ['fr', 'en']). |
t |
JSONPath:string,values?:object
|
Returns the translation string for the given key (supports dot notation: site.nav.link). Accepts an optional replacement object %{key}. |
subscribe |
listener:(locale) => void |
Subscribes to locale changes and returns an unsubscribe function () => void. |
isInitialized |
void |
Returns true if the instance is initialized, otherwise false. |
static async getJSON |
urlOrPath: string,callback?(res: object): fn
|
Static async method fetching JSON from a URL (browser via fetch) or file path (Node.js via fs). Returns Promise<T> or passes result to optional callback. |
toJSON |
void |
Returns a serialized snapshot of the instance configuration options. |
createMemoryStorage |
void |
Creates an in-memory volatile storage adapter (StorageAdapter) based on a Map. |
createProcessEnvStorage |
void |
Creates a storage adapter persisting active locale in process.env[saveAs]. |
async createServerTranslator |
options:object,instance?:Si18nLike |
Async function creating a translator { t, locale, locales, i18n } for React Server Components (RSC / Next.js / SSR). |
<Si18nProvider> |
props:{ i18n, options, children } |
Context component <Si18nProvider> synchronizing locale changes tear-free via useSyncExternalStore. |
useTranslation |
void |
React hook returning { t, locale, locales, setLocale, isReady, error, i18n }. |
# Default options (15)
| Argument | Default value | Description |
|---|---|---|
locales |
{} |
Object containing your language translations. Required only if path or localeLoader is not defined. |
path |
null |
Access path to the directory containing your translation JSON files. Files will be loaded automatically according to the active language (the fallback language will always be loaded). Priority over the locales option. Required only if locales is not defined. e.g.: ./locales. |
localeLoader |
undefined |
Custom async loader function (locale) => Promise<object> to load locale messages dynamically. Ideal for bundler code-splitting or server-side loaders. |
availableLocales |
[] |
List containing the names of available languages as defined in the translation files. Required only if path is defined. |
lang |
'' |
Active language to use. If omitted, si18n.js detects language from URL, storage, or environment (browser / system variables). If not set, the fallbackLang option becomes required. |
fallbackLang |
'' |
Fallback language. The language to use when the requested language or a requested string in a non-fully translated language is missing. If omitted, it falls back to the value of lang. |
activeClass |
'' |
CSS class to add to the active button for the current language. Used with togglersSelector. |
togglersSelector |
'' |
CSS selector for toggler elements. si18n.js automatically binds click listeners. Elements must have the data-lang="XX" attribute. |
isTogglerSelect |
false |
Set to true if the toggler selector targets a <select> element. Unavailable options are automatically disabled. |
sanitizeHtml |
undefined |
Optional (html: string) => string function called to sanitize HTML before injection when data-si18n-html="true" is used (e.g., DOMPurify.sanitize). |
saveLang |
true |
Whether to save the language in persistent storage (localStorage for browser, memory or process.env for Node.js). |
saveAs |
'lang' |
Key name for persistent storage and URL parameter (Default: 'lang'). |
reloadPage |
false |
If set to true, the page will automatically refresh after changing language. |
translate |
function() {} |
Callback function called to apply manual translations. Note: For browsers, the <html> tag automatically receives lang and dir attributes. |
onLocaleChanged |
function() {} |
Central callback triggered on locale changes (on init and setLocale). Receives (locale, instance). |
dependencies |
{} |
Pluggable dependency injection object for Si18nCore and Si18nNode: storage (custom StorageAdapter), detectLanguage, getLanguageFromUrl, createPathLocaleLoader, reload, logError. |