localize.dev

Documentation

localize.dev transforme le texte codé en dur en appels de traduction dans les bases de code React, Next.js, React Native, Vue, Nuxt, Svelte, Angular, Astro, Solid, Qwik, Lit, Ember, Flutter, Rails, Django, Laravel, Android, iOS et HTML statique, écrit les dictionnaires de langue, configure la bibliothèque i18n s'il n'y en a pas, et peut ouvrir la pull request pour vous. La traduction s'exécute sur le modèle hébergé de localize.dev, il n'y a donc rien à configurer au-delà d'une clé API.

Démarrage rapide

Vous avez besoin de Node.js 22 ou version ultérieure. Votre application n'a pas encore besoin de bibliothèque i18n ; le premier lancement en configure une. Créez une clé API gratuite dans le tableau de bord, puis choisissez votre framework :

React

Tout UI rendu via un composant capable d'appeler useTranslation(): React avec Vite, Remix ou Gatsby, les renderers Electron et Tauri, et les systèmes de design ou les packages d'UI partagés. Les composants de classe obtiennent t via withTranslation(), ou <Translation> quand ils ne sont pas l'exportation par défaut du fichier. Les phrases autour des liens restent entières avec <Trans> (son propre lien title inclus, la copie dans les constantes, toasts et messages zod est trouvée, et les dates, nombres et prix suivent la langue de l'application.

npm install --save-dev localize-dev

# Save your API key on this machine
npx localize-dev login

# See what would change, without writing anything
npx localize-dev run --dry-run -l es,fr

# Rewrite the code and write the locale files
npx localize-dev run -l es,fr

# Only if the run added packages to package.json
npm install

Guide React: quels changements, avec un avant et un après.

Next.js

Les composants serveur du App Router reçoivent const { t } = await getTranslation() et composants client useTranslation(), tous deux dans la langue choisie par le serveur ; le Pages Router fonctionne comme React. Un proxy.ts donne à chaque page une URL par langue avec hreflang liens.

npm install --save-dev localize-dev

# Save your API key on this machine
npx localize-dev login

# See what would change, without writing anything
npx localize-dev run --dry-run -l es,fr

# Rewrite the code and write the locale files
npx localize-dev run -l es,fr

# Only if the run added packages to package.json
npm install

Guide Next.js: quels changements, avec un avant et un après.

React Native and Expo

React Native et Expo. accessibilityLabel et accessibilityHint sont également traduits, et la configuration i18n évite les API uniquement du navigateur.

npm install --save-dev localize-dev

# Save your API key on this machine
npx localize-dev login

# See what would change, without writing anything
npx localize-dev run --dry-run -l es,fr

# Rewrite the code and write the locale files
npx localize-dev run -l es,fr

# Only if the run added packages to package.json
npm install

Guide React Native and Expo: quels changements, avec un avant et un après.

Vue

Texte et {{ }} dans <template> deviennent {{ $t('key') }} et les attributs deviennent :placeholder="$t('key')", avec vue-i18n configuré dans src/i18n.ts et installé dans main.ts. Messages affichés depuis <script setup> (alert, toasts, document.title) utilisent t() de useI18n, et n === 1 ? … : … devient un pluriel vue-i18n. Un existant createI18n configuration est laissée telle quelle. Une phrase autour d'un lien ou d'un texte en gras reste un seul message à travers <i18n-t>, avec les éléments comme slots. Les constantes dans <script setup> affichées par le template ({{ title }}, v-for="tab in tabs") deviennent des clés lues via $t(). Les templates écrits en Pug (<template lang="pug">) obtiennent la même chose : h1 {{ $t('key') }}, texte piped et block, et input(:placeholder="$t('key')").

npm install --save-dev localize-dev

# Save your API key on this machine
npx localize-dev login

# See what would change, without writing anything
npx localize-dev run --dry-run -l es,fr

# Rewrite the code and write the locale files
npx localize-dev run -l es,fr

# Only if the run added packages to package.json
npm install

Guide Vue: quels changements, avec un avant et un après.

Nuxt

Les pages et les composants sont réécrits comme n'importe quel composant Vue, et @nuxtjs/i18n est ajouté à nuxt.config, avec une URL par langue (/es/about) et hreflang liens depuis useLocaleHead().

npm install --save-dev localize-dev

# Save your API key on this machine
npx localize-dev login

# See what would change, without writing anything
npx localize-dev run --dry-run -l es,fr

# Rewrite the code and write the locale files
npx localize-dev run -l es,fr

# Only if the run added packages to package.json
npm install

Guide Nuxt: quels changements, avec un avant et un après.

Svelte and SvelteKit

Le balisage devient {$_('key')} avec svelte-i18n, initialisé à partir de src/lib/i18n et importé dans +layout (ou main.ts sans SvelteKit). Les attributs comme title="Hello {name}" deviennent un seul message. Dans SvelteKit, un généré hooks.server rend chaque page dans la langue du visiteur (son choix enregistré, puis Accept-Language), il n'y a donc pas de scintillement de la langue source.

npm install --save-dev localize-dev

# Save your API key on this machine
npx localize-dev login

# See what would change, without writing anything
npx localize-dev run --dry-run -l es,fr

# Rewrite the code and write the locale files
npx localize-dev run -l es,fr

# Only if the run added packages to package.json
npm install

Guide Svelte and SvelteKit: quels changements, avec un avant et un après.

Angular

Modèles (.html et en ligne template:) deviennent {{ 'key' | translate }} avec ngx-translate, attributs [placeholder]="'key' | translate" ([attr.aria-label] pour aria), et chaînes dans les liaisons ('key' | translate). Flux de contrôle (@if, @for), *ngIf et les modèles en ligne sont compris ; les éléments marqués par les propres de Angular i18n sont laissés à celui-ci. La configuration ajoute provideI18n() à app.config.ts (chaque locale regroupé, pas de chargeur HTTP) et TranslatePipe à chaque composant autonome qui en a besoin.

npm install --save-dev localize-dev

# Save your API key on this machine
npx localize-dev login

# See what would change, without writing anything
npx localize-dev run --dry-run -l es,fr

# Rewrite the code and write the locale files
npx localize-dev run -l es,fr

# Only if the run added packages to package.json
npm install

Guide Angular: quels changements, avec un avant et un après.

Astro

Texte et {expressions} dans .astro les fichiers deviennent {t('key')}, attributs title={t('key')}, avec t d'un généré src/i18n module qui lit la langue depuis l'URL. Chaque page statique obtient une copie par langue (src/pages/es/about.astro), donc chaque langue est une page réelle et indexable, et les mises en page définissent <html lang dir>. localizePath(path, lang) construit des liens entre les langues. Les îles React, Vue et Svelte (de @astrojs/react, /vue, /svelte) sont localisées dans la même exécution, chacune avec sa propre bibliothèque configurée et suivant la langue de la page.

npm install --save-dev localize-dev

# Save your API key on this machine
npx localize-dev login

# See what would change, without writing anything
npx localize-dev run --dry-run -l es,fr

# Rewrite the code and write the locale files
npx localize-dev run -l es,fr

Guide Astro: quels changements, avec un avant et un après.

Solid

JSX comme React, avec useTranslation() d'un généré src/i18n.ts: i18next derrière un signal de langue, de sorte que le texte se met à jour sur place lorsque la langue change. Les phrases autour des liens restent un seul message, rendu par rich(t('key'), { a: (c) => <a href="/terms">{c}</a> }) du même module.

npm install --save-dev localize-dev

# Save your API key on this machine
npx localize-dev login

# See what would change, without writing anything
npx localize-dev run --dry-run -l es,fr

# Rewrite the code and write the locale files
npx localize-dev run -l es,fr

# Only if the run added packages to package.json
npm install

Guide Solid: quels changements, avec un avant et un après.

Qwik

JSX dans component$ obtient const { t } = useTranslation() d'un généré src/i18n.ts qui lit la langue depuis l'URL (celui de Qwik City useLocation), et chaque route, y compris les dynamiques, obtient une copie par langue sous src/routes/es/. Les phrases autour des liens restent un seul message à travers rich() du même module.

npm install --save-dev localize-dev

# Save your API key on this machine
npx localize-dev login

# See what would change, without writing anything
npx localize-dev run --dry-run -l es,fr

# Rewrite the code and write the locale files
npx localize-dev run -l es,fr

Guide Qwik: quels changements, avec un avant et un après.

Lit

Texte et ${expressions} dans html`…` les modèles deviennent ${t('key')}, attributs placeholder=${t('key')}. Les composants obtiennent un I18nController pour qu'ils se ré-affichent lorsque setLanguage() change de langue ; les modèles imbriqués, les styles et les commentaires sont laissés seuls.

npm install --save-dev localize-dev

# Save your API key on this machine
npx localize-dev login

# See what would change, without writing anything
npx localize-dev run --dry-run -l es,fr

# Rewrite the code and write the locale files
npx localize-dev run -l es,fr

Guide Lit: quels changements, avec un avant et un après.

Ember

Les modèles Glimmer (.hbs, et <template> dans .gjs/.gts) deviennent {{t "key" name=this.name}} avec ember-intl, les attributs placeholder={{t "key"}}, les chaînes dans {{if}} (t "key"), et {{if (eq n 1) "item" "items"}} dans une phrase un pluriel. Les messages vont à translations/<locale>.json; une route d'application générée choisit la langue.

npm install --save-dev localize-dev

# Save your API key on this machine
npx localize-dev login

# See what would change, without writing anything
npx localize-dev run --dry-run -l es,fr

# Rewrite the code and write the locale files
npx localize-dev run -l es,fr

# Only if the run added packages to package.json
npm install

Guide Ember: quels changements, avec un avant et un après.

Flutter

Text('Hi $name') et des arguments tels que hintText: deviennent AppLocalizations.of(context)!.hiName(name), avec des messages en lib/l10n/app_en.arb (clés camelCase, pluriels ICU, métadonnées de placeholder). const est supprimé des widgets qui contiennent maintenant une recherche; les chaînes sans BuildContext dans le périmètre sont signalés. La configuration ajoute flutter_localizations, generate: true, l10n.yaml, le MaterialApp délégués et la liste des langues iOS.

# Save your API key on this machine
npx localize-dev login

# See what would change, without writing anything
npx localize-dev run --dry-run -l es,fr

# Rewrite the code and write the locale files
npx localize-dev run -l es,fr

Non package.json nécessaire : npx exécute la CLI. Guide Flutter: quels changements, avec un avant et un après.

Static HTML

Le texte reste dans la page et obtient un data-i18n="key" marqueur (les attributs obtiennent data-i18n-placeholder et similaires). Un généré i18n.js charge les fichiers de locale et échange le texte ; ajoute <select data-i18n-switcher></select> pour un menu de langue. Une phrase avec des éléments en ligne (<strong>, liens) est un message, reconstruit à partir des propres éléments de la page. Les traductions sont également regroupées dans i18n.js, donc les pages ouvertes depuis le disque fonctionnent aussi. Le texte d'un moteur de template ({{ }}, {% %}, PHP) est laissé tel quel.

Les moteurs de recherche ne voient que le texte de la page, alors définissez "htmlPages": true et "siteUrl" dans localize.config.json pour écrire également chaque page traduite dans chaque langue (es/index.html), lié avec hreflang.

npm install --save-dev localize-dev

# Save your API key on this machine
npx localize-dev login

# See what would change, without writing anything
npx localize-dev run --dry-run -l es,fr

# Rewrite the code and write the locale files
npx localize-dev run -l es,fr

Guide Static HTML: quels changements, avec un avant et un après.

Ruby on Rails

Le texte dans les vues ERB devient <%= t('key', name: @user.name) %>, avec %{name} des espaces réservés dans config/locales/<locale>.yml et des pluriels appelés avec count:. available_locales et un switch_locale autour de l'action sont ajoutés.

# Save your API key on this machine
npx localize-dev login

# See what would change, without writing anything
npx localize-dev run --dry-run -l es,fr

# Rewrite the code and write the locale files
npx localize-dev run -l es,fr

Non package.json nécessaire : npx exécute la CLI. Guide Ruby on Rails: quels changements, avec un avant et un après.

Django

Les modèles obtiennent {% translate '…' %} et {% blocktranslate %}, les traductions vont dans locale/<lang>/LC_MESSAGES/django.po, et les paramètres obtiennent LANGUAGES, LOCALE_PATHS et LocaleMiddleware.

# Save your API key on this machine
npx localize-dev login

# See what would change, without writing anything
npx localize-dev run --dry-run -l es,fr

# Rewrite the code and write the locale files
npx localize-dev run -l es,fr

Non package.json nécessaire : npx exécute la CLI. Guide Django: quels changements, avec un avant et un après.

Laravel

Le texte Blade devient {{ __('Hi :name', ['name' => $user->name]) }} avec lang/<locale>.json indexé par le texte, et un SetLocale le middleware choisit la langue (?locale=, un lang cookie, puis Accept-Language).

# Save your API key on this machine
npx localize-dev login

# See what would change, without writing anything
npx localize-dev run --dry-run -l es,fr

# Rewrite the code and write the locale files
npx localize-dev run -l es,fr

Non package.json nécessaire : npx exécute la CLI. Guide Laravel: quels changements, avec un avant et un après.

Android

android:text, hint et contentDescription dans les mises en page et les menus deviennent @string/…, et Compose Text("Hi $name") dans @Composable fonctions devient stringResource(R.string.…, name). Voir le code en Kotlin ou Java (label.setText("Hi"), label.text = "Hi") devient setText(R.string.…). Les chaînes vont à res/values-<lang>/strings.xml avec numérotées %1$s espaces réservés, et locales_config.xml liste les langues pour Android 13+.

# Save your API key on this machine
npx localize-dev login

# See what would change, without writing anything
npx localize-dev run --dry-run -l es,fr

# Rewrite the code and write the locale files
npx localize-dev run -l es,fr

Non package.json nécessaire : npx exécute la CLI. Guide Android: quels changements, avec un avant et un après.

iOS

texte SwiftUI (Text("Hi \(name)"), Button, .navigationTitle) reste tel quel, car SwiftUI le recherche dans le catalogue de chaînes lui-même ; les chaînes UIKit (label.text = "Hi") deviennent String(localized: "Hi"). Tout va dans Localizable.xcstrings avec chaque langue dans le même fichier. Ajoutez le catalogue à votre cible d'application dans Xcode une fois.

# Save your API key on this machine
npx localize-dev login

# See what would change, without writing anything
npx localize-dev run --dry-run -l es,fr

# Rewrite the code and write the locale files
npx localize-dev run -l es,fr

Non package.json nécessaire : npx exécute la CLI. Guide iOS: quels changements, avec un avant et un après.

Vous voulez regarder avant de vous inscrire ? npx localize-dev scan liste chaque chaîne qu'il extrairait, et --offline génère des clés sans traduire. Aucun n'a besoin de clé ni n'utilise votre quota. Une exécution en ligne ultérieure remplit les traductions manquantes sans toucher aux clés existantes.

Bases de code prises en charge

Chaque framework obtient son propre scanner et sa propre bibliothèque i18n. Ce qui change dans chacun se trouve sous son nom dans la démarrage rapide, et dans son guide.

Monorepos: exécutez depuis la racine avec --workspaces (ou celui de l'Action workspaces: true) pour localiser chaque application, chacune avec ses propres fichiers de langue. Les bibliothèques de composants partagés (packages sans dev ou start script) sont également traduits, dans les langues des applications qui en dépendent, et leurs messages sont ajoutés aux fichiers de langue de ces applications, de sorte que la t() les appels se résolvent via la configuration propre à chaque application. Pour localiser une seule app, donnez plutôt à l'Action working_directory: apps/web : elle lit le localize.config.json, le code et les fichiers de langue de cette app, et la pull request les modifie sur place.

Le framework est détecté à partir de package.json (ou pubspec.yaml, ou les fichiers quand il n'y a ni l'un ni l'autre) ; définissez "framework" dans localize.config.json pour le remplacer. Les dictionnaires React utilisent des espaces réservés i18next ou ICU ; Vue, Svelte et HTML utilisent {name}.

Fichiers de langue existants

Vous avez déjà i18n configuré, dans n'importe quelle stack ? localize-dev translate saute le code et remplit ce qui manque dans vos fichiers de langue : clés sans traduction pour le moment, dans toutes les langues que vous avez déjà, ou dans de nouvelles que vous nommez. Les traductions existantes ne sont jamais modifiées, et aucun framework n'est détecté, il fonctionne donc pour n'importe quelle stack, iOS et Android natifs, Rails et Django inclus.

# Fill in what's missing in the languages already there
npx localize-dev translate --dry-run
npx localize-dev translate

# Add languages (creates the files)
npx localize-dev translate -l de,ja,pt-BR

# Or as a pull request
npx localize-dev pr --translate
FormatFichiersNotes
JSONlocales/en.json, public/locales/en/*.jsoni18next (avec _one/_other pour les pluriels), ICU (react-intl, next-intl, y compris les correspondances =0 et select), vue-i18n, ngx-translate, Transloco
YAMLconfig/locales/en.yml, devise.en.ymlRuby on Rails, sous la clé de langue; cartes des pluriels (un/autre)
gettextlocale/fr/LC_MESSAGES/django.poDjango, WordPress, PHP, Python. Les entrées vides et floues sont remplies; les emplacements pluriels suivent les formes pluriels. Les nouvelles langues sont créées à côté d’une existante, avec leurs propres formes pluriels.
Flutterlib/l10n/app_en.arbMessages ICU; les descriptions @key deviennent le contexte. gen-l10n détecte les nouveaux fichiers.
Androidres/values/strings.xml<string> et <plurals>; translatable="false", <xliff:g> et les échappements respectés. Écrit values-es/, values-pt-rBR/.
AppleLocalizable.xcstrings, en.lproj/*.stringsCatalogues de chaînes obtiennent chaque langue en place, y compris les pluriels; les fichiers .strings reçoivent de nouveaux dossiers .lproj (ajouter les langues dans Xcode).
Extensions de navigateur_locales/en/messages.jsonLes placeholders sont copiés dans chaque langue.

Les espaces réservés dans la syntaxe propre à chaque format ({{name}}, %{name}, %1$s, %@, %(name)s, $NAME$) sont protégés : une traduction qui en perd ou en ajoute un, ou réorganise ceux sans nom comme %s, est ignorée et signalée. Les pluriels obtiennent toutes les formes dont la langue cible a besoin (le russe reçoit one, few et many). Les commentaires et descriptions dans les fichiers sont transmis au modèle comme contexte.

Copie modifiée: localize.lock.json conserve une empreinte de chaque message source lorsqu'il est traduit. Modifiez l'anglais et le prochain run, translate ou l'exécution d'une Action le traduit à nouveau dans chaque langue ; check échoue sur les traductions laissées obsolètes. Validez le fichier.

Dans l'Action GitHub, définissez mode: translate. Chaque exécution ouvre ou met à jour une pull request avec tout ce qui manque, de sorte que les nouvelles clés ajoutées en anglais soient traduites au fur et à mesure.

Crowdin, Phrase, Lokalise, Transifex

Vous travaillez avec des traducteurs dans un TMS ? Envoyez-y vos fichiers de langue source et récupérez leurs traductions. Ajoutez le projet à localize.config.json (provider: crowdin, phrase, lokalise ou transifex) et mettez le jeton API dans CROWDIN_TOKEN, PHRASE_TOKEN, LOKALISE_TOKEN ou TRANSIFEX_TOKEN (jamais dans le fichier de configuration) :

localize.config.json
{
  "tms": { "provider": "crowdin", "projectId": "123456" }
}
# Upload source files (and, the first time, your existing translations)
npx localize-dev sync push --translations

# Download translations into your locale files
npx localize-dev sync pull

JSON, YAML, ARB, Android, .strings et les fichiers d'extension sont synchronisés. Crowdin conserve vos dossiers, Phrase conserve chaque fichier séparément avec une balise, Lokalise stocke chacun sous son chemin avec la langue comme %LANG_ISO%, et Transifex obtient une ressource par fichier (projectId comme o:acme:p:web). Les langues sont mises en correspondance avec les codes propres du projet (es → es-ES, pt-BR → pt_BR). Pour Crowdin Enterprise ou le centre de données américain de Phrase, définissez "url" à son adresse API. Les fichiers gettext et String Catalog sont mieux gérés par les intégrations propres du TMS. Une boucle courante : exécutez localize-dev run ou translate pour les traductions automatiques, poussez, laissez les réviseurs modifier, tirez.

commandes CLI

localize-dev init
Écrit un localize.config.json avec tous les paramètres par défaut détaillés.
localize-dev init --agent [--pre-commit]
Configure des agents de codage pour traduire le texte pendant qu'ils l'écrivent : le serveur MCP pour Claude Code et Cursor, un hook après édition pour Claude Code, et des instructions dans AGENTS.md et une règle Cursor. --pre-commit vérifie également les fichiers préparés avant chaque commit. Comment ça marche.
localize-dev login [--api-key ldv_live_…]
Enregistre votre clé API pour cette machine. Sans --api-key il demande, ou lit la clé depuis stdin.
localize-dev whoami
Affiche l'espace de travail, le plan et l'utilisation de la clé actuelle.
localize-dev logout
Supprime la clé enregistrée de cette machine.
localize-dev scan [-d src] [-o extracted_strings.json]
Liste toutes les chaînes qui seraient extraites, plus celles ignorées et pourquoi. Ne change rien ; aucune clé nécessaire.
localize-dev run [--dry-run] [--offline] [--workspaces] [--files a.tsx… | --changed | --staged] [-l es,fr] [-d src] [--api-key …]
Le passage complet : extraire, nommer les clés, traduire, réécrire les fichiers, mettre à jour les dictionnaires et configurer la bibliothèque i18n si l'application n'en a pas. Avec --workspaces, chaque application d'un monorepo à tour de rôle.
localize-dev translate [--dry-run] [-l de,ja] [-s en] [--api-key …]
Remplit les traductions manquantes dans les fichiers de langue existants, pour n'importe quelle pile. Le code n'est pas touché. Formats.
localize-dev check [--only strings|translations] [--files a.tsx… | --changed | --staged] [--json]
Échoue la CI sur les copies codées en dur, les traductions manquantes et les traductions obsolètes par rapport à leur source ; signale les clés inutilisées et, avec une langue de droite à gauche, les styles gauche/droite. Détails.
localize-dev prune [--dry-run]
Supprime les clés auxquelles aucun code ne fait référence du fichier de langue de chaque langue.
localize-dev rtl [--fix]
Liste les styles épinglés à gauche ou à droite ; --fix échange les sûrs pour les logiques. Droite à gauche.
localize-dev content [--dry-run] [-l de,ja]
Traduit les documents Markdown et MDX en une copie par langue. Docs et contenu.
localize-dev sync push|pull [--translations] [-l de,ja]
Pousse les fichiers de langue source vers Crowdin, Phrase, Lokalise ou Transifex, ou récupère leurs traductions. Configuration.
localize-dev seo <url> [--limit 500] [--json]
Vérifie ce que les moteurs de recherche voient de chaque langue sur un site, en direct ou lors d'une build locale : le sitemap, les liens hreflang, <html lang>, l'adresse canonique et og:locale. Se termine avec 1 lorsqu'un problème est trouvé, afin qu'il puisse s'exécuter en CI après un déploiement Détails.
localize-dev pr [--translate] [--base main] [--repo owner/name] [--branch name] [--token …]
Exécute le pass et ouvre (ou met à jour) une demande de tirage sur localize-dev/<base> au lieu d'écrire localement. Utilise GITHUB_TOKEN quand --token est omis. Avec --translate, la demande de tirage ne remplit que les traductions manquantes.

Agents de codage

localize-dev mcp exécute un serveur MCP, afin qu'un agent de codage (Claude Code, Cursor, Codex, GitHub Copilot et autres) puisse vérifier et localiser le dépôt sur lequel il travaille. Lovable, Bolt, v0 et d'autres constructeurs qui s'exécutent dans le navigateur se connectent au serveur hébergé sur https://uselocalize.dev/api/mcp avec une clé API. La configuration de chaque agent se trouve sur la page MCP. Pour que l'agent traduise le texte au fur et à mesure qu'il l'écrit, exécutez ceci dans le dossier de l'application :

npx localize-dev init --agent               # MCP server, Claude Code hook, AGENTS.md, Cursor rule
npx localize-dev init --agent --pre-commit  # also check staged files before each commit

Il écrit ces fichiers :

  • .mcp.json et .cursor/mcp.json : le serveur MCP, pour Claude Code et Cursor.
  • .claude/settings.json : un hook après édition qui exécute npx -y localize-dev hook. Lorsqu'un fichier modifié par Claude Code contient du texte non traduit, le hook liste les lignes et l'agent appelle localize sur ce fichier avant de continuer.
  • AGENTS.md, et un CLAUDE.md qui l'importe lorsqu'il n'y en a pas : quand appeler localize et check. Une règle Cursor dans .cursor/rules/localize.mdc dit la même chose pour vos fichiers d'interface utilisateur.
  • Avec --pre-commit : check --staged dans le hook de pré-commit de husky, ou celui de git.

Dans un monorepo, les instructions nomment l'application. Vos paramètres, serveurs et textes existants sont conservés, et une seconde exécution ne change rien. Validez les fichiers pour que l'équipe et ses agents les partagent. Le hook ne lit que le fichier, sans clé ni modèle, et prend environ une seconde ; LOCALIZE_DEV_HOOK=off le met en pause. Les chaînes qu'un agent traduit via le serveur MCP s'affichent comme "En cours d'écriture" sur la page d'utilisation.

Configuration i18n

Vue, Nuxt, Svelte et HTML statique sont configurés comme décrit dans Bases de code prises en charge. En React, les composants réécrits appellent useTranslation(), l'application a donc besoin d'i18next en cours d'exécution. Quand ce n'est pas le cas, l'exécution (et la pull request de l'Action) ajoute ce qui manque :

  • i18next et react-i18next dans package.json. L'Action met également à jour votre fichier de verrouillage (npm, pnpm, Yarn ou Bun) ; après une exécution locale, exécutez votre installation.
  • src/i18n.ts (ou .js), qui charge les fichiers de langue et choisit la langue à partir d'un choix enregistré, puis du navigateur, puis de votre langue source. Avec plus de trois langues cibles, seule la source est regroupée et le reste se charge lors de la première utilisation ("lazyLocales" à choisir). Dans les applications Remix sans v3_routeConfig, les pages s'hydratent dans leur <html lang> et basculent après le chargement.
  • Un import './i18n' dans votre fichier d'entrée : le script dans index.html, sinon src/main.* ou src/index.*.
  • resolveJsonModule dans tsconfig.json, afin que les fichiers de localisation puissent être importés.
  • Une importation dans votre fichier de configuration de test (Vitest setupFiles, Jest setupFilesAfterEnv, src/setupTests), afin que les tests qui recherchent du texte le trouvent toujours. Vue installe vue-i18n pour @vue/test-utils.
  • Une ligne dans .gitattributes qui fusionne les fichiers de localisation clé par clé, afin que deux branches ajoutant des clés ne entrent pas en conflit. Activez-le une fois par clone avec git config merge.localize-dev.driver "npx localize-dev merge-locales %O %A %B %P" (les fusions sur GitHub ne l'exécutent pas).
  • src/language-switcher.tsx, exportation <LanguageSwitcher /> et useLanguage() avec les valeurs par défaut de cette app, et localize-dev en tant que dépendance. Rends le commutateur où les gens choisissent une langue, comme dans ton en-tête ; il n'est pas placé pour toi. Options.

Si un fichier initialise déjà i18next, rien de cela n'est ajouté et votre configuration est utilisée telle quelle. Le commutateur enregistre le choix dans localStorage et le restaure lors de la prochaine visite ; pour changer depuis ton propre code, appelle à i18n.changeLanguage('es') et stocke lang de la même manière. Les projets configurés par une version antérieure obtiennent le commutateur lors de leur prochaine exécution. Dans le routeur de pages Next.js, l'importation va dans pages/_app.

Routeur d'applications Next.js

Fichiers sous app/ sans 'use client' sont des composants côté serveur, qui ne peuvent pas utiliser d'hooks. Ils reçoivent const { t } = await getTranslation() à la place (le composant devient async s’il n’était pas), à partir d’un i18n.server module qui choisit la langue à partir d’une lang cookie, puis celle du navigateur Accept-Language. Les composants côté client conservent useTranslation(). La mise en page racine est enveloppée dans un module généré <I18nProvider> qui transmet aux composants côté client la même langue, et <html lang> le suit. Le commutateur généré définit lang cookie et rafraîchit, afin que les composants côté serveur se re-rendent dans la nouvelle langue.

Sélecteur de langue

<LanguageSwitcher /> est un menu des langues incluses dans votre application (votre langue source et les langues cibles que vous avez configurées), chacune avec son drapeau et son propre nom. Il fonctionne avec le clavier (flèches, Début/Fin, Entrée, Échap, taper un nom) et les lecteurs d'écran, mémorise le choix et définit <html lang> et dir pour les langues de droite à gauche.

Header.tsx
import { LanguageSwitcher, useLanguage } from './language-switcher';

<LanguageSwitcher />                                   // flag + native name
<LanguageSwitcher compact />                           // just the flag
<LanguageSwitcher nameStyle="native-english" />        // Español · Spanish
<LanguageSwitcher languages={['en', 'es']} />          // choose and order the list
<LanguageSwitcher regions={{ en: 'GB', pt: 'BR' }} />  // which country's flag
<LanguageSwitcher renderFlag={(l) => <img src={`/flags/${l.code}.svg`} alt="" />} />
<LanguageSwitcher unstyled className="…" buttonClassName="…" menuClassName="…" optionClassName="…" />

// Anywhere else: the current language and the list
const { language, info, languages, setLanguage, isRTL } = useLanguage();
info.flag        // "🇪🇸"
info.nativeName  // "Español"

Autres props : showFlag, align (start/end), side (top/bottom), label, disabled, renderButton, renderOption, persist (localStorage, cookie ou none), storageKey et onChange. Les styles intégrés lisent les variables CSS, donc un thème ou un mode sombre se résume à quelques lignes : --ldv-bg, --ldv-fg, --ldv-border, --ldv-hover, --ldv-muted, --ldv-accent, --ldv-radius, --ldv-shadow. Les options sélectionnées et mises en surbrillance portent data-selected et data-active pour votre propre CSS. En dehors de React, getLanguageInfo('pt-BR') renvoie les mêmes noms, drapeau et direction.

Dans le App Router de Next.js, le fichier généré enregistre le choix dans le lang cookie et se rafraîchit, donc les composants serveur suivent. Les drapeaux emoji s'affichent comme deux lettres sur Windows ; passez renderFlag pour les images là-bas.

Tous les autres frameworks reçoivent le même menu, généré pour leurs propres composants et leur état, basé sur <localize-language-switcher> (élément personnalisé de localize-dev/switcher) : il accepte les mêmes options sous forme d'attributs, émet language-change et se stylise avec les mêmes variables --ldv-* ou avec ::part(button|menu|option|flag).

Ce que la configuration écrit
Vue          src/language-switcher.ts           <LanguageSwitcher />, useLanguage()
Nuxt         components/LanguageSwitcher.ts     auto-imported, with composables/useLanguage.ts
Svelte       LanguageSwitcher.svelte            the $language store from language.ts
Solid, Qwik  src/language-switcher.tsx          <LanguageSwitcher />, useLanguage()
Angular      language-switcher.component.ts     <app-language-switcher />, languageOf(translate)
Astro        components/LanguageSwitcher.astro  useLanguage(Astro.url) from i18n/language.ts
Lit          src/language-switcher.ts           <app-language-switcher>, useLanguage()
Ember        components/language-switcher       <LanguageSwitcher />, the language service
React Native language-switcher.tsx              a native picker, useLanguage()
HTML         <localize-language-switcher>       from the CDN; i18n.js switches on language-change
Rails        shared/_language_switcher.html.erb render "shared/language_switcher", current_language
Django       language_switcher.html             {% include %}, {{ language }} in every template
Laravel      <x-language-switcher />            App\Support\Language::current()
Flutter      lib/language_switcher.dart         LanguageSwitcher(), language.of(context)
iOS          LanguageSwitcher.swift             LanguageSwitcher(), LanguageStore.shared, .appLanguage()
Android      LanguageSwitcher.kt                LanguageSwitcher(), showLanguagePicker(context)

Clés d'API

Les clés ressemblent à ldv_live_… et appartiennent à un espace de travail ; chaque clé d'un espace de travail partage son quota mensuel. La CLI recherche une clé dans cet ordre :

  1. le --api-key drapeau
  2. le LOCALIZE_API_KEY variable d'environnement (utilisez ceci en CI)
  3. la clé enregistrée par localize-dev login, dans ~/.config/localize-dev/credentials.json (lisible uniquement par vous)

Seule une empreinte de chaque clé est stockée de notre côté, donc un secret est affiché une seule fois, lors de sa création. Révoquez une clé depuis le tableau de bord et elle cessera de fonctionner immédiatement.

Configuration

Tout est optionnel. Mettez les surcharges dans localize.config.json à la racine du projet ; les drapeaux CLI prévalent sur le fichier.

ChampPar défautDescription
srcDir"src"Dossier à scanner.
frameworkdetectedreact, react-native, vue, svelte ou html. Détecté à partir de package.json, puis des fichiers.
include / ignoreby framework / tests, stories, .d.tsExpressions globales relatives à srcDir: *.{tsx,jsx}, *.vue, *.svelte ou *.html.
sourceLocale"en"Langue du texte codé en dur.
targetLocales["es", "fr"]Langues à traduire.
localesPath"locales/{locale}.json"Emplacement du dictionnaire, par ex. public/locales/{locale}/common.json.
attributesplaceholder, alt, title, aria-label, label, tooltipAttributs traités comme du texte UI. React Native ajoute accessibilityLabel et accessibilityHint.
importSource"react-i18next"React : d'où useTranslation est importé, par ex. next-i18next.
interpolationby frameworki18next écrit {{name}} (React); ICU écrit {name} (Svelte, HTML); Vue écrit {name} avec les caractères spéciaux de vue-i18n échappés.
htmlPagesfalseHTML statique : écrivez également chaque page traduite par langue (es/index.html), pour les moteurs de recherche.
siteUrlnoneL'adresse du site, par ex. https://example.com. Nécessaire pour les liens hreflang entre versions linguistiques.
localizeFormatstrueFaites en sorte que les dates, nombres et prix codés en dur suivent la langue de l'application.
currency"USD"Ce que signifie un $ seul avant un montant lorsque les prix sont localisés, par ex. "CAD".
lazyLocalesplus de 3 languesReact : regroupez uniquement la langue source et chargez les autres lors de leur première utilisation.
contentnoneDocuments Markdown et MDX pour le contenu localize-dev : { "include": ["docs/**/*.md"], "output": "{dir}/{name}.{locale}{ext}" }.

Action GitHub

L'action s'exécute de la même manière en CI et ouvre une demande de tirage contre la branche qui l'a déclenchée. Les reruns mettent à jour la même branche et PR. Avant la première exécution, stockez votre clé API dans le dépôt :

  1. 1

    Copiez votre clé d'API

    Ça commence par ldv_live_ et ne s'affiche qu'une seule fois. Perdu ? Créez-en un nouveau sur la Clés API page.
  2. 2

    Ouvrez les secrets Actions du dépôt

    Sur GitHub, ouvrez votre dépôt et allez à Paramètres → Secrets et variables → Actions, puis cliquez sur Nouveau secret de dépôt. Vous avez besoin d'un accès administrateur au dépôt pour voir Paramètres.
  3. 3

    Ajoutez le secret

    Nom: LOCALIZEDEV_API_KEYSecret: collez votre clé, sans espaces ni retours à la ligne. Ensuite, cliquez sur Ajouter un secret. GitHub cache la valeur à partir de ce moment-là; pour la modifier, mettez à jour le secret.
  4. 4

    Sans la GitHub App localize.dev : autorisez l'Action à ouvrir des pull requests

    Dans Paramètres → Actions → Général, sous Permissions de workflow, cochez Autoriser GitHub Actions à créer et approuver les demandes de tirage et cliquez Enregistrer. Sans cela, l'exécution traduit tout mais échoue lors de l'ouverture de la demande de tirage. Si l'option est grisée, un propriétaire de l'organisation doit l'autoriser en premier dans Paramètres → Actions → Général.

Puis validez le workflow :

.github/workflows/localize.yml
name: Localize

on:
  push:
    branches: [main]
  pull_request:
    types: [opened, synchronize]
  workflow_dispatch: # "Run workflow" button in the Actions tab

permissions:
  contents: write # push the localize-dev/<branch> branch
  pull-requests: write # open or update the localization PR

# One run per branch at a time; a newer push supersedes the running one.
concurrency:
  group: localize-${{ github.head_ref || github.ref_name }}
  cancel-in-progress: true

jobs:
  localize:
    runs-on: ubuntu-latest
    timeout-minutes: 60
    # Skip localize.dev's own branches, and fork PRs (their token is read-only).
    if: >-
      !startsWith(github.head_ref || github.ref_name, 'localize-dev/') &&
      (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository)
    steps:
      - name: Checkout
        uses: actions/checkout@v5
        with:
          # On PRs, scan the branch itself rather than the merge commit.
          ref: ${{ github.head_ref || github.ref_name }}

      - name: Localize
        uses: ufortstephen/autolocalize-action@v1
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          api_key: ${{ secrets.LOCALIZEDEV_API_KEY }}
          target_locales: 'es,fr,de'
          # src_dir: 'src' # default: your framework's (src, app/views, resources/views, lib…)
          # locales_path: 'public/locales/{locale}/common.json' # default: your framework's
          # working_directory: 'apps/web' # monorepo: localize just this app

Autres entrées : src_dir, locales_path, base_branch, api_url, offline (pas de clé nécessaire), workspaces (chaque app dans un monorepo), working_directory (une seule app d'un monorepo), tms_pull / tms_push (avec tms_token: apporter des traductions révisées dans la demande de tirage, télécharger les fichiers source depuis la branche par défaut), mode: translate (ne remplir que fichiers de localisation existants). Résultats : pr_url, strings_count.

Chaque exécution signale sa fin : la page du projet dans le tableau de bord liste les exécutions récentes avec leur demande de tirage, chaînes remplacées, traductions par langue, et l'erreur si une a échoué.

Aperçu de la demande de tirage

Vous souhaitez le voir dans votre propre code avant de tout configurer ? Connectez le dépôt dans le tableau de bord et choisissez Ouvrir une demande de tirage de prévisualisation sur sa page. localize.dev lit la branche par défaut, localise jusqu'à 200 chaînes dans la première langue du projet sur le serveur, et ouvre une demande de tirage depuis localize-dev/preview. Aucun flux de travail, clé API ou checkout n'est nécessaire ; les chaînes comptent pour votre plan, et les traductions apparaissent dans l'onglet Revue comme n'importe quelle exécution. L'exécuter à nouveau met à jour la même demande de tirage.

Dans un monorepo, chaque application est localisée à tour de rôle jusqu'à ce que 200 chaînes soient utilisées. Les fichiers dans Git LFS et les sous-modules ne sont pas dans l'archive fournie par GitHub, ils sont donc exclus, et un très grand dépôt n'est que partiellement lu ; la demande de tirage le mentionne. L'action GitHub n'a aucune limite.

Vérification CI

localize-dev check échoue (code de sortie 1) lorsque le texte est encore codé en dur, une langue manque de traductions, ou les traductions sont obsolètes car leur anglais a changé depuis (à partir de localize.lock.json), et liste chacune. Il signale également les clés non utilisées par le code (supprimez-les avec localize-dev prune), texte dans les feuilles de style (content: "New"), messages qui affichent une valeur stockée telle quelle (Status: {{status}} affiche « paid ») et, lorsqu'une langue cible est de droite à gauche, les styles épinglés à gauche ou à droite, et next/font polices auxquelles il manque des lettres d'une langue cible ; celles-ci ne font pas échouer la vérification. Une clé que le code utilise et qui manque dans le fichier de langue source fait échouer, car les gens verraient la clé elle-même. Il ne lit que les fichiers : pas de clé API, rien n'est envoyé. Sur GitHub Actions, chaque problème est également une annotation sur le diff de la pull request.

npx localize-dev check                      # both
npx localize-dev check --only strings       # hardcoded copy only
npx localize-dev check --only translations  # missing translations only
npx localize-dev check --json
npx localize-dev check --staged             # files staged for the next commit (pre-commit hook)
npx localize-dev check --changed            # files changed since the last commit, and new ones

Avec --files, --changed ou --staged, check ne regarde que ces fichiers : copie codée en dur et clés manquantes du fichier de locale source. Il ignore les vérifications à l'échelle du projet, il s'exécute donc en une seconde environ, assez rapidement pour un hook de pré-commit ou un hook après édition d'un agent. run prend les mêmes options.

Ou dans un workflow qui s'exécute sur les pull requests, avec l'Action :

.github/workflows/i18n-check.yml
on: pull_request
jobs:
  i18n:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: ufortstephen/autolocalize-action@v1
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          mode: check

Pseudo-localisation

Avant de payer pour une traduction unique, voyez où votre mise en page se casse. --pseudo écrit un en-XA langue : chaque message accentué, environ 35 % plus long (comme l'allemand ou le finnois) et entre crochets, avec les espaces réservés et les balises conservés exactement, de sorte que l'application s'exécute comme avec une vraie traduction. Passez-y et recherchez les boutons coupés, les cartes qui débordent et tout ce qui n'est pas entre crochets : cette copie est toujours codée en dur.

npx localize-dev run --pseudo         # en-XA: [Šáṽé çĥáñĝéš one two]
npx localize-dev run --pseudo rtl     # ar-XB: right to left
npx localize-dev translate --pseudo   # from existing locale files, code untouched

Aucun modèle et aucune clé : il n'utilise pas votre quota. Vos vraies langues restent telles quelles. Le terrain de jeu l'a aussi, comme le en-XA langue.

Dates, nombres et argent

Dans les applications React, React Native, Electron, Tauri, Solid, Vue et Svelte, réécrit également les dates, nombres et prix codés en dur dans une locale, afin qu'ils suivent la langue de l'application :

  • date.toLocaleDateString('en-US', …) et new Intl.NumberFormat('en-US') prennent la langue de l'application à la place (tout comme les appels sans locale, qui utilisent celle du navigateur).
  • `$${price}` et <span>${price}</span> deviennent un format de devise, donc $1,234.50 se lit 1.234,50 $ en allemand. €, £, ¥, ₹, ₦, CA$, A$ et d'autres symboles sont mappés à leur devise ; un simple $ est USD sauf si "currency" dans localize.config.json indique le contraire.
  • Dans les modèles et scripts Vue, la langue provient de vue-i18n ($i18n.locale); en Svelte, de svelte-i18n $locale.

Dates de date-fns (format(d, 'MMM d, yyyy')), dayjs et moment deviennent Intl.DateTimeFormat avec les mêmes parties ; les temps relatifs (formatDistance, fromNow) sont listés pour que vous passiez à Intl.RelativeTimeFormat. localeCompare et les majuscules ou minuscules affichées en JSX utilisent la langue de l'application (i turque). Les listes suivent également la langue : {tags.join(', ')} en JSX et un `${xs.slice(0, -1).join(', ')} and ${xs[xs.length - 1]}` deviennent Intl.ListFormat, ainsi le japonais obtient 、 et l'espagnol son propre "y". Les pull requests listent chaque changement. Désactivez-le avec "localizeFormats": false dans localize.config.json. Les applications Next.js App Router sont laissées telles quelles pour l'instant, car leurs composants client obtiennent la langue via un fournisseur.

Une URL par langue

Les moteurs de recherche indexent chaque langue lorsqu'elle a sa propre URL. Dans les applications Next.js App Router, le généré proxy.ts (middleware.ts avant Next 16) sert /es/about avec le /about page en espagnol, sans déplacer vos fichiers, et <LanguageAlternates /> dans la mise en page racine ajoute hreflang liens entre les versions. Les URL sans préfixe continuent de suivre le lang cookie et le navigateur, et le menu de langue se déplace entre les URL préfixées.

Les applications Nuxt obtiennent @nuxtjs/i18n's prefix_except_default URL avec useLocaleHead() dans app.vue; définir "siteUrl" pour les liens absolus. Les sites statiques obtiennent la même chose de htmlPages. Une application avec son propre proxy ou middleware la conserve : l'exécution explique les deux en-têtes (x-localize-lang, x-localize-path) à définir là.

Les titres et descriptions de page suivent également. Next.js export const metadata devient un generateMetadata qui utilise le traducteur de la requête (celui d'un modèle de titre %s conservé), et un existant generateMetadata obtient t. Dans les applications monopages (Vite React, Vue, Svelte, Angular, Solid), index.html's <title> et la description sont traduits et définis par le module généré lorsque la langue change ; le fichier les conserve tels qu'écrits pour le premier affichage.

Pour les moteurs de recherche, la mise en page racine obtient également une adresse canonique et og:locale pour l'URL de chaque langue, à partir du généré i18n-seo.ts. app/sitemap.ts liste chaque page avec une adresse fixe (pas de routes [slug], zones de connexion ou dossiers privés) dans chaque langue, avec des alternatives hreflang. Un robots.ts y pointe. Les deux sont écrits uniquement lorsque l'application n'en a aucun des deux, et vous pouvez les modifier. Définissez "siteUrl" pour que le sitemap ait des adresses complètes. Lorsqu'il n'est pas défini, l'adresse provient d'un fichier CNAME ou de la page d'accueil dans package.json, et sur Vercel, Netlify, Render ou Railway de l'adresse que l'hôte donne à la build.

Les mises en page Astro obtiennent un composant I18nHead dans leur head, avec les mêmes liens et og:locale. SvelteKit 2.3 et versions ultérieures servent chaque langue à sa propre adresse via un hook de réacheminement dans src/hooks.ts, les routes n'ont donc pas besoin de dossier [lang], et hooks.server ajoute les liens à chaque page. Les deux obtiennent un sitemap.xml construit à partir du dossier pages ou routes, de sorte que les nouvelles pages soient listées sans relancer localize, et un robots.txt lorsque l'application n'en a aucun.

Les applications Qwik City obtiennent un composant LanguageHead dans root.tsx avec les mêmes liens, et les mêmes routes sitemap et robots.

Les applications React Router (mode framework) obtiennent chaque route à nouveau sous chaque langue, donc /es/about est la page à propos en espagnol. Le fichier routes passe ses routes via withLanguageRoutes depuis app/i18n-routes.ts, et la racine rend chaque page dans la langue de son adresse, avec les mêmes liens. Les liens sans langue conservent la langue de la page, et les routes sitemap.xml et robots.txt sont ajoutées lorsque l'application n'en a aucune. Avec plus de trois langues, celles qui ne sont pas dans le bundle se chargent dans le chargeur de la route, de sorte que le serveur les rend également.

Les applications Remix 2 obtiennent la même chose lorsque v3_routeConfig est activé dans vite.config et les routes sont dans app/routes.ts (configuration de route de Remix). Sans cela, l'exécution indique quoi changer.

Les sites statiques avec htmlPages et siteUrl obtiennent également un sitemap.xml listant chaque page dans chaque langue, et un robots.txt lorsqu'ils n'en ont pas.

La langue du visiteur est mise en correspondance exacte, par langue, ou avec une variante régionale proposée par l'application : un navigateur réglé sur pt-PT obtient le pt-BR.

Pour voir ce que voient les moteurs de recherche, pointez localize-dev seo sur le site. Il fonctionne sur n'importe quel site, configuré par localize.dev ou non.

npx localize-dev seo https://example.com     # the live site
npx localize-dev seo http://localhost:3000   # a local build, before you deploy

Il lit robots.txt et le sitemap, puis charge chaque page et les pages que ses liens hreflang nomment. Il signale les pages qui ne se chargent pas ou redirigent, les liens hreflang sans l'adresse complète ou sans x-default, une langue dont la page ne renvoie pas, un <html lang> ou og:locale qui ne correspond pas à la langue de la page, et un lien canonique qui pointe ailleurs. Les adresses sur une autre origine (siteUrl, lorsque vous vérifiez une build locale) sont chargées depuis l'adresse que vous fournissez.

Révision des traductions

Les traductions automatiques des exécutions de GitHub Action et des demandes d'extraction CLI (pr, pr --translate) apparaissent sur le Réviser onglet, source et traduction côte à côte, filtrable par langue. Approuvez-les une par une ou toutes en même temps, ou modifiez-en une : une modification doit conserver les espaces réservés de la source et remplace l'entrée de mémoire de traduction de l'espace de travail, de sorte que chaque exécution ultérieure utilise la correction. Les messages pluriels montrent chaque forme dont la langue a besoin (le russe one, few, many…) et sont modifiées forme par forme.

Appliquer les modifications à la pull request valide vos modifications dans la pull request de localisation ouverte de l'exécution dont elles proviennent, dans ses fichiers de locale JSON, via l'application GitHub. Les modifications qui ne peuvent pas être placées (autres formats de fichiers ou une pull request fusionnée) parviennent à la prochaine exécution via la mémoire de traduction.

Chaque traduction conserve un fil de notes pour le prochain réviseur, et indique qui l'a approuvé ou modifié et quand. Lorsque la même correction continue d'apparaître, l'onglet suggère un terme du glossaire: un mot source qui doit toujours se traduire d'une certaine manière (checkout → pago), ou un nom que les réviseurs continuent de restaurer et qui doit rester non traduit. Un clic l'ajoute au glossaire du projet, que chaque exécution suit.

Attribuer un reviseur par langue des membres de l'espace de travail. Lorsqu'une exécution apporte de nouvelles traductions dans leur langue, ils reçoivent un e-mail pour l'exécution avec un lien vers l'onglet. Chaque ligne renvoie au fichier source d'où elle provient sur GitHub. Le modèle signale les traductions dont il n'est pas sûr (un mot ambigu, une expression idiomatique, une supposition formelle ou informelle) avec sa raison, et l'onglet s'ouvre sur celles-ci, sous À vérifier; Approuver le reste les laisse à une personne. Dans le code affiche les lignes autour de l'endroit où il est utilisé, lues à partir de la pull request du run, afin que la copie puisse être vérifiée là où elle est affichée.

Glossaire

Chaque projet dispose d'un glossaire sur sa page de tableau de bord : termes qui ne doivent jamais être traduits (noms de marque et de produit), traductions préférées pour votre vocabulaire et une note de style telle que "informel ; utilisez du en allemand". Il s'applique à chaque requête des exécutions GitHub Action et CLI du projet. Les traductions qui modifient un terme à ne jamais traduire sont rejetées ; les traductions préférées et la note de style guident le modèle.

Plans et limites

PlanChaînes / moisLangues
Free1 0003
ProIllimité5
TeamIllimitéIllimité

Une chaîne compte une fois, peu importe le nombre de langues dans lesquelles elle est traduite. Les allocations s'exécutent sur des périodes de 30 jours : à partir du jour où votre espace de travail a été créé, ou à partir de votre date de facturation sur un plan payant. Le tableau de bord indique quand le vôtre est réinitialisé. La limite de langues compte chaque langue vers laquelle vous avez traduit au cours des 30 derniers jours, sur tous vos espaces de travail et projets. Si une exécution dépassait l'allocation ou la limite de langue, elle s'arrête avant d'écrire quoi que ce soit, explique pourquoi et se termine avec le code 2, afin que la CI échoue au lieu de valider une traduction partielle.

Langues

localize.dev traduit dans toute langue que connaît le CLDR d’Unicode, plus de 600, écrite sous forme de code BCP 47 : une langue (es, ja, sw), avec une écriture ou une région quand elle compte (zh-Hant, sr-Latn, pt-BR, es-419). Il n’y a pas de liste où choisir : indiquez sourceLocale et targetLocales dans localize.config.json, ou passez --locales.

Un code qui ne désigne aucune langue est refusé avant toute traduction, avec ce qui était sans doute voulu : jp → ja, cn → zh. Tout code valide est accepté, y compris les alias comme en-UK (lu comme en-GB) et les codes à usage privé. La CLI, la GitHub Action, le tableau de bord et l’API vérifient de la même façon.

  • Le nombre de langues d’une exécution dépend de votre offre. Un texte compte une fois, quel que soit le nombre de langues.
  • Les traductions sont les plus solides dans les langues les plus répandues. Pour les langues moins présentes en ligne, faites relire la pull request par un locuteur natif et ajoutez un glossaire des termes propres à votre produit.
  • Toute langue dont l’écriture va de droite à gauche (arabe, hébreu, persan, ourdou, kurde, pachto, syriaque, n’ko et d’autres) reçoit dir="rtl" et les vérifications de styles gauche/droite. Un code qui indique son écriture compte aussi : pa-Arab.
  • Les pluriels suivent les formes propres à chaque langue (one, few, many…) issues du CLDR : le polonais, l’arabe ou le gallois ont toutes celles qu’il leur faut.
  • Les pseudo-langues en-XA (accentuée et plus longue) et ar-XB (de droite à gauche) ne demandent ni modèle ni clé.

Pluriels et texte enrichi

Un choix dépendant de la quantité tel que {n} {n === 1 ? 'file' : 'files'} left devient un message (également n > 1 ? …, rules.select(n) === 'one' ? … et des aides comme plural(n, 'file', 'files')), réécrit en t('files.left', { count: n }). Chaque langue reçoit exactement les formes dont elle a besoin (espagnol trois, polonais quatre, arabe six), écrites comme files.left_one, files.left_few... pour i18next ou un message pluriel ICU, et i18next choisit le bon à l'exécution.

Une phrase avec deux comptes, {files} {files === 1 ? 'file' : 'files'} in {folders} {folders === 1 ? 'folder' : 'folders'}, devient un message par phrase comptée ({{count}} file / {{count}} files) placée dans une seule phrase, {{files}} in {{folders}}, donc chaque compte obtient ses propres formes et les traducteurs peuvent toujours les réorganiser. Les ordinaux des assistants comme ordinal(place) ou {n}{ordinalSuffix(n)} deviennent des pluriels ordinaux i18next (t('rank.place', { count: place, ordinal: true })), avec les propres formes ordinales de chaque langue : Anglais 1st/2nd/3rd/4th, Français 1re/2e, Espagnol 1.º.

La même chose fonctionne au-delà de React : les messages ICU (Svelte, Ember) obtiennent un selectordinal, vue-i18n obtient key_ordinal_one, et dans Vue, Svelte, Astro, Lit, Ember, Flutter et Rails, chaque phrase comptée devient un appel pluriel propre à l'intérieur de la phrase. Intl.PluralRules... clés choisies par

Texte autour d'éléments en ligne simples, comme Read the <a href="/docs">docs</a> first, devient un seul message (Read the <a>docs</a> first) rendu avec <Trans>, afin que les traducteurs puissent déplacer le lien. L'élément conserve ses props. Les traductions qui perdent ou renomment une balise sont rejetées.

Frameworks sans <Trans> obtiennent la même chose, avec le propre texte de l'élément comme un message distinct : Vue via <i18n-t> avec des slots, Svelte via un généré <I18nT> (snippets dans Svelte 5, un slot dans 3 et 4), Angular via un généré <i18n-t> avec ng-template slots (un NgModule avant Angular 14), Ember via un généré <I18nT>, Solid et Qwik à travers rich(). Dans Rails, un link_to à l'intérieur d'une phrase est interpolé dans un _html message, donc son balisage est conservé.

Ce qui est traduit

  • Texte JSX, avec du texte et des expressions simples fusionnées en un message : Hi {user.name}! → Hi {name}!
  • Attributs dans la liste autorisée (placeholder, alt, title, aria-label, aria-description, aria-valuetext, aria-roledescription, label, tooltip; configurable), et un SVG <title> et <desc>
  • Chaînes et littéraux de modèle rendu en JSX, y incluant les deux branches de a ? 'X' : 'Y' et le côté droit de &&
  • Copier dans les constantes que les composants lisent (const NAV = [{ label: 'Home' }] avec NAV.map((n) => n.label)): les chaînes deviennent des clés et chaque lecture devient t(n.label)
  • Chaînes jointes ('Hi ' + name + '!') comme un seul message, et les messages passés aux state setters, toasts, alert et validation zod ou yup (z.string().min(1, 'Required'))
  • Props de composants qui contiennent du texte (<EmptyState heading="No orders yet" />, de Vue empty-text="…"), tables de recherche lues par clé (STATUS_LABELS[order.status]), fonctions d'aide dont les résultats sont rendus, et copies importées depuis un module simple (nav.ts), dont les chaînes deviennent des clés lorsque chaque composant qui l'importe peut être suivi
  • Phrases genrées (gender === 'female' ? 'She…' : 'He…') en un seul message avec une variante par genre, afin que chaque langue la formule à sa manière
  • Modèles Vue et Svelte (Vue en HTML ou Pug) : texte, {{ }} et { } expressions, attributs (y compris title="Hi {name}"), et les chaînes qu'une expression rend, comme {{ saving ? 'Saving…' : 'Save' }}
  • Scripts Vue et Svelte: messages affichés, transmis à alert, confirm, notifications et toasts, ou assignés à document.title, et constantes que la template affiche (const tabs = [{ label: 'Home' }] avec {{ tab.label }}): les chaînes deviennent des clés et le modèle les lit via $t() ou $_()
  • HTML statique : texte de l'élément, phrases autour d'éléments en ligne comme un message, et les mêmes attributs. Le texte d'un moteur de templates ou PHP est laissé tel quel et signalé.

Jamais traduit : URLs et chemins, couleurs, UUIDs, unités CSS, e-mails, noms de fichiers, identifiants en snake_case ou kebab-case, texte uniquement symboles, et tout ce qui est à l'intérieur <code>, <pre>, <Trans>, translate="no" ou data-i18n-ignore.

Chaque traduction est vérifiée avant d'être écrite : celle qui ajoute, supprime ou renomme une {placeholder} est rejetée. Les traductions existantes dans vos dictionnaires ne sont jamais écrasées.

De droite à gauche

L’arabe, l’hébreu, le persan et l’ourdou se lisent de droite à gauche, donc ml-4 ou margin-left se retrouve du mauvais côté. Lorsqu’une langue cible est de droite à gauche, check les styles de liste ancrés à gauche ou à droite, et localize-dev rtl --fix remplace les styles par des équivalents logiques qui ont le même aspect en anglais :

  • Tailwind : ml-/mr- → ms-/me-, pl-/pr- → ps-/pe-, text-left → text-start, coins arrondis et bordures, avec des variantes comme md: restent.
  • CSS : margin-left → margin-inline-start, padding-right → padding-inline-end, text-align: left → start, bordures et rayons de coin.

left-/right- les positions et les flottements ne sont rapportés que parce qu’ils sont souvent des astuces de centrage. Vérifiez le résultat dans ar-XB avec localize-dev run --pseudo rtl.

Documentation et contenu

La documentation, les articles de blog et les pages juridiques sont traduits en tant que documents entiers. localize-dev content traduit les fichiers Markdown et MDX en une copie par langue : la prose, les titres, les éléments de liste, les cellules de tableau et les titres et descriptions du front-matter sont traduits ; les blocs de code, le code inline, les URLs de liens, les imports MDX, les composants et expressions restent exactement tels quels. Si une traduction perd l’un de ces éléments, elle est conservée en anglais et signalée.

localize.config.json
{
  "targetLocales": ["es", "fr"],
  "content": {
    "include": ["docs/**/*.md", "blog/**/*.mdx"],
    "output": "{dir}/{name}.{locale}{ext}"
  }
}

La sortie par défaut se trouve à côté de chaque fichier (intro.es.md) comme Nextra, Fumadocs et Hugo l'exigent. Pour Docusaurus utilisez "i18n/{locale}/docusaurus-plugin-content-docs/current/{path}". Les documents inchangés depuis leur création sont ignorés (leurs empreintes sont dans localize.lock.json), de sorte que seuls les documents modifiés sont traduits à nouveau.

Confidentialité et données

  • Seules les chaînes extraites sont envoyées à l'API, avec leur chemin de fichier et leur composant comme contexte. Aucun fichier entier n'est jamais envoyé.
  • Les analyses et exécutions hors ligne ne quittent jamais votre machine.
  • Le CLI réécrit les fichiers localement ; l'action GitHub commit depuis votre propre exécuteur avec votre propre jeton.
  • Les clés API sont stockées sous forme de hachages ; le secret n'est affiché qu'une seule fois.

Limitations

  • Le texte enrichi accepte des éléments en ligne imbriqués jusqu'à trois niveaux (le texte en gras à l'intérieur d'un lien devient un message imbriqué). Le lien lui-même title, écrit manuellement ou défini depuis le code, reste à l'intérieur du composant de la phrase dans React.
  • Les pluriels sont reconnus à partir de choix de deux chaînes en fonction du nombre (===/!== 1, > 1, < 2, Intl.PluralRules) et des aides pour les pluriels avec un compte et les deux formes. Chaque framework extrait les pluriels selon sa propre syntaxe (Django's pluralize, Laravel's Str::plural, Kotlin's if … else) et écrit son propre format (<plurals>, variations du catalogue de chaînes, msgid_plural, trans_choice; Angular, qui n'en a pas, choisit la clé avec un généré pluralForm pipe). Une phrase avec plusieurs comptes obtient un pluriel par compte partout sauf chez Django (dont le blocktranslate prend un) et iOS ; les ordinaux fonctionnent dans les frameworks JavaScript et Angular, pas dans Flutter, Rails, Django, Laravel, Android ou iOS, dont les bibliothèques i18n n'ont pas de formes ordinales. Sur iOS, un pluriel a besoin de son nombre dans le texte ("\(n) items"), ce par quoi varie le catalogue de chaînes.
  • Les composants de classe sont traduits : via withTranslation() lorsque la classe est l'exportation par défaut du fichier, sinon avec render() en cours d'exécution dans <Translation>. Les constructeurs, membres statiques et gestionnaires d'événements utilisent le i18next t, de sorte que le texte stocké par un constructeur suive la langue lors du montage.
  • Dans les scripts Vue et Svelte, la copie extraite est ce qui est clairement affiché : alert, confirm, toasts et notifications, document.title, et les constantes que le modèle affiche (une chaîne, ref('…'), les valeurs d'un objet ou d'une liste label/title champs, passés aux composants enfants traduits), et ce que computed(), fonctions, $: et $derived retour. Une chaîne constante que le script lui-même utilise conserve son texte, car le script peut la comparer ou l'envoyer. Dans Svelte 4, le texte qu'une fonction renvoie est mis à jour lors du prochain rendu du composant après un changement de langue.
  • Les modèles Vue en Pug sont traduits, #[…] balises en ligne (trois niveaux de profondeur) et #{} valeurs incluses. Les modèles dans d'autres préprocesseurs (rares en dehors de Pug) sont ignorés et signalés.
  • La copie en dehors des composants (emails, texte SVG, messages construits sur le serveur) n'est pas extraite ; les documents Markdown et MDX sont traduits en entier avec localize-dev content.
  • La demande de prévisualisation lit les fichiers de la branche par défaut ; les fichiers Git LFS et les sous-modules sont exclus.