IntlPull
Tutorial
18 min read

Comment traduire une page web : Le guide ultime pour le développeur (2026)

Guide étape par étape pour vérifier et implémenter la traduction de sites web. Des widgets simples aux systèmes i18n React, Next.js et Vue prêts pour la production.

IntlPull Team
IntlPull Team
03 Feb 2026, 11:44 AM [PST]
On this page
Summary

Guide étape par étape pour vérifier et implémenter la traduction de sites web. Des widgets simples aux systèmes i18n React, Next.js et Vue prêts pour la production.

Les niveaux de traduction de sites web

Traduire un site web n'est pas une chose unique. C'est un spectre allant du "hack rapide" au "système d'internationalisation de qualité production".

Voici à quoi cela ressemble :

NiveauEffortQualitéCas d'utilisation
1. Widget Google Translate5 minutesFaibleTemporaire, test de la demande
2. Pages statiques manuelles1-2 joursBonnePetits sites (5-10 pages)
3. Fichiers JSON + Bibliothèque3-5 joursSuperLa plupart des sites web
4. Intégration CMS1-2 semainesSuperSites marketing
5. Plateforme i18n complète2-4 semainesExcellenteSaaS, sites à fort trafic

Ce guide couvre les cinq approches avec des exemples de code.

Niveau 1 : Widget Google Translate (Le hack de 5 minutes)

Quand l'utiliser : Vous voulez tester si un marché est viable avant d'investir dans une vraie traduction.

Avantages :

  • Zéro code
  • Supporte 100+ langues
  • Gratuit

Inconvénients :

  • La qualité de traduction est médiocre
  • Pas de valeur SEO (les robots voient la langue d'origine)
  • Semble non professionnel
  • Les traductions ne sont pas mises en cache (lent)

Implémentation

Ajoutez ceci à votre <body> :

HTML
1<div id="google_translate_element"></div>
2
3<script type="text/javascript">
4  function googleTranslateElementInit() {
5    new google.translate.TranslateElement(
6      {
7        pageLanguage: 'en',
8        includedLanguages: 'es,fr,de,zh-CN,ja',
9        layout: google.translate.TranslateElement.InlineLayout.SIMPLE
10      },
11      'google_translate_element'
12    );
13  }
14</script>
15
16<script type="text/javascript" src="//translate.google.com/translate_a/element.js?cb=googleTranslateElementInit"></script>

C'est tout. Les utilisateurs voient une liste déroulante pour choisir les langues.

Styliser le widget :

CSS
1/* Cacher le branding Google */
2.goog-te-banner-frame {
3  display: none !important;
4}
5
6/* Styliser la liste déroulante */
7.goog-te-combo {
8  padding: 8px 12px;
9  border: 1px solid #e2e8f0;
10  border-radius: 6px;
11  font-size: 14px;
12}

Pourquoi ça craint pour la production

  1. Désastre SEO : Google indexe votre contenu original. Les utilisateurs hispanophones cherchant en espagnol ne trouveront pas votre site.

  2. Pas de contrôle : Vous ne pouvez pas corriger les mauvaises traductions. "Bank" pourrait se traduire par "banc" (pour s'asseoir) au lieu de banque.

  3. Performance : Chaque chargement de page tape sur les serveurs de Google. Ajoute de la latence.

  4. Problèmes UX : La mise en page se casse quand le texte s'étend (l'allemand est 30 % plus long que l'anglais).

Verdict : Bien pour tester. Ne livrez pas ça à de vrais utilisateurs.

Niveau 2 : Pages statiques manuelles (La méthode Copier-Coller)

Quand l'utiliser : Petit site (moins de 10 pages), rarement mis à jour, soucieux du budget.

Effort : 1-2 jours par langue.

Comment ça marche

Créez des fichiers HTML séparés pour chaque langue :

/index.html           (Anglais)
/es/index.html        (Espagnol)
/fr/index.html        (Français)
/de/index.html        (Allemand)

Exemple de structure

Version anglaise (/index.html) :

HTML
1<!DOCTYPE html>
2<html lang="en">
3<head>
4  <link rel="alternate" hreflang="es" href="/es/" />
5  <link rel="alternate" hreflang="fr" href="/fr/" />
6  <title>Welcome to Our Product</title>
7</head>
8<body>
9  <nav>
10    <a href="/">English</a>
11    <a href="/es/">Español</a>
12    <a href="/fr/">Français</a>
13  </nav>
14
15  <h1>Welcome to Our Product</h1>
16  <p>Build amazing apps with our platform.</p>
17
18  <button>Get Started</button>
19</body>
20</html>

Version espagnole (/es/index.html) :

HTML
1<!DOCTYPE html>
2<html lang="es">
3<head>
4  <link rel="alternate" hreflang="en" href="/" />
5  <link rel="alternate" hreflang="fr" href="/fr/" />
6  <title>Bienvenido a Nuestro Producto</title>
7</head>
8<body>
9  <nav>
10    <a href="/">English</a>
11    <a href="/es/">Español</a>
12    <a href="/fr/">Français</a>
13  </nav>
14
15  <h1>Bienvenido a Nuestro Producto</h1>
16  <p>Construye aplicaciones increíbles con nuestra plataforma.</p>
17
18  <button>Comenzar</button>
19</body>
20</html>

Configuration SEO

Ajoutez des balises hreflang pour que Google sache quelle version montrer :

HTML
1<!-- Dans chaque page -->
2<link rel="alternate" hreflang="en" href="https://example.com/" />
3<link rel="alternate" hreflang="es" href="https://example.com/es/" />
4<link rel="alternate" hreflang="fr" href="https://example.com/fr/" />
5<link rel="alternate" hreflang="x-default" href="https://example.com/" />

Détection de langue

Redirigez en fonction de la langue du navigateur :

JavaScript
1// Détecter la langue de l'utilisateur et rediriger
2const userLang = navigator.language || navigator.userLanguage;
3const supportedLangs = ['en', 'es', 'fr'];
4const lang = userLang.slice(0, 2);
5
6// Rediriger uniquement sur la page d'accueil
7if (window.location.pathname === '/' && supportedLangs.includes(lang) && lang !== 'en') {
8  // Vérifier si l'utilisateur a manuellement sélectionné une langue auparavant
9  if (!localStorage.getItem('language_selected')) {
10    window.location.href = `/${lang}/`;
11  }
12}
13
14// Se souvenir du choix de l'utilisateur
15document.querySelectorAll('nav a').forEach(link => {
16  link.addEventListener('click', () => {
17    localStorage.setItem('language_selected', 'true');
18  });
19});

Avantages

  • Contrôle SEO complet
  • Traductions parfaites (vous engagez des professionnels)
  • Rapide (pas d'appels API externes)
  • Hébergement simple (juste des fichiers HTML)

Inconvénients

  • Cauchemar de maintenance (mettre à jour 5 pages pour chaque changement)
  • Ne passe pas à l'échelle (100 pages × 5 langues = 500 fichiers)
  • Pas de contenu dynamique

Verdict : Bien pour les pages de destination, terrible pour les applications.

Niveau 3 : Fichiers JSON + Bibliothèque (L'approche standard)

Quand l'utiliser : La plupart des sites web. C'est la norme de l'industrie.

Frameworks : React, Vue, Next.js, Svelte - tous supportent cela.

Comment ça marche

  1. Extraire les chaînes vers des fichiers JSON
  2. Utiliser une bibliothèque i18n pour charger les traductions
  3. Remplacer le texte codé en dur par des clés de traduction

Exemple React (react-i18next)

Installer :

Terminal
npm install react-i18next i18next

Configuration (i18n.js) :

JavaScript
1import i18n from 'i18next';
2import { initReactI18next } from 'react-i18next';
3
4import enTranslations from './locales/en.json';
5import esTranslations from './locales/es.json';
6import frTranslations from './locales/fr.json';
7
8i18n
9  .use(initReactI18next)
10  .init({
11    resources: {
12      en: { translation: enTranslations },
13      es: { translation: esTranslations },
14      fr: { translation: frTranslations }
15    },
16    lng: localStorage.getItem('language') || 'en',
17    fallbackLng: 'en',
18    interpolation: {
19      escapeValue: false // React échappe déjà
20    }
21  });
22
23export default i18n;

Fichiers de traduction :

locales/en.json :

JSON
1{
2  "welcome": {
3    "title": "Welcome to Our Product",
4    "subtitle": "Build amazing apps with our platform",
5    "cta": "Get Started"
6  },
7  "nav": {
8    "home": "Home",
9    "pricing": "Pricing",
10    "docs": "Documentation"
11  }
12}

locales/fr.json :

JSON
1{
2  "welcome": {
3    "title": "Bienvenue dans Notre Produit",
4    "subtitle": "Construisez des applications incroyables avec notre plateforme",
5    "cta": "Commencer"
6  },
7  "nav": {
8    "home": "Accueil",
9    "pricing": "Tarifs",
10    "docs": "Documentation"
11  }
12}

Composant :

JSX
1import { useTranslation } from 'react-i18next';
2
3function HomePage() {
4  const { t, i18n } = useTranslation();
5
6  const changeLanguage = (lng) => {
7    i18n.changeLanguage(lng);
8    localStorage.setItem('language', lng);
9  };
10
11  return (
12    <div>
13      <nav>
14        <button onClick={() => changeLanguage('en')}>Anglais</button>
15        <button onClick={() => changeLanguage('es')}>Espagnol</button>
16        <button onClick={() => changeLanguage('fr')}>Français</button>
17      </nav>
18
19      <h1>{t('welcome.title')}</h1>
20      <p>{t('welcome.subtitle')}</p>
21      <button>{t('welcome.cta')}</button>
22    </div>
23  );
24}

Avec des variables :

JSX
1// Fichier de traduction
2{
3  "greeting": "Bonjour, {{name}} ! Vous avez {{count}} messages."
4}
5
6// Composant
7<p>{t('greeting', { name: 'Sarah', count: 5 })}</p>
8// Sortie : "Bonjour, Sarah ! Vous avez 5 messages."

Avec des pluriels :

JSON
1{
2  "messages": "{{count}} message",
3  "messages_plural": "{{count}} messages"
4}
JSX
<p>{t('messages', { count: 1 })}</p> // "1 message"
<p>{t('messages', { count: 5 })}</p> // "5 messages"

Exemple Next.js (next-intl)

Installer :

Terminal
npm install next-intl

Structure de fichiers :

/app/[locale]/
├── layout.tsx
├── page.tsx
/messages/
├── en.json
├── es.json
├── fr.json

i18n.ts :

TypeScript
1import { getRequestConfig } from 'next-intl/server';
2
3export default getRequestConfig(async ({ locale }) => ({
4  messages: (await import(`./messages/${locale}.json`)).default
5}));

middleware.ts (détection de locale) :

TypeScript
1import createMiddleware from 'next-intl/middleware';
2
3export default createMiddleware({
4  locales: ['en', 'es', 'fr'],
5  defaultLocale: 'en'
6});
7
8export const config = {
9  matcher: ['/((?!api|_next|.*\..*).*)']
10};

Composant Page :

TSX
1import { useTranslations } from 'next-intl';
2
3export default function HomePage() {
4  const t = useTranslations('welcome');
5
6  return (
7    <div>
8      <h1>{t('title')}</h1>
9      <p>{t('subtitle')}</p>
10      <button>{t('cta')}</button>
11    </div>
12  );
13}

URLs :

  • /en → Anglais
  • /es → Espagnol
  • /fr → Français

Next.js gère le routage, le SEO et la détection de locale automatiquement.

Exemple Vue (vue-i18n)

Installer :

Terminal
npm install vue-i18n

Configuration (main.js) :

JavaScript
1import { createApp } from 'vue';
2import { createI18n } from 'vue-i18n';
3import App from './App.vue';
4
5import en from './locales/en.json';
6import es from './locales/es.json';
7
8const i18n = createI18n({
9  locale: localStorage.getItem('language') || 'en',
10  fallbackLocale: 'en',
11  messages: { en, es }
12});
13
14createApp(App).use(i18n).mount('#app');

Composant :

VUE
1<template>
2  <div>
3    <select v-model="$i18n.locale" @change="saveLanguage">
4      <option value="en">Anglais</option>
5      <option value="es">Espagnol</option>
6    </select>
7
8    <h1>{{ $t('welcome.title') }}</h1>
9    <p>{{ $t('welcome.subtitle') }}</p>
10  </div>
11</template>
12
13<script>
14export default {
15  methods: {
16    saveLanguage() {
17      localStorage.setItem('language', this.$i18n.locale);
18    }
19  }
20};
21</script>

Gestion des traductions

Problème : Vous avez maintenant des fichiers JSON. Comment faites-vous pour :

  • Les envoyer aux traducteurs ?
  • Suivre ce qui est traduit ?
  • Détecter les clés manquantes ?

Option 1 : Manuelle (pénible)

  1. Exporter JSON vers Excel
  2. Envoyer par e-mail aux traducteurs
  3. Copier-coller au retour

Option 2 : Utiliser un TMS (intelligent)

Terminal
1# Exemple IntlPull
2npx @intlpullhq/cli init
3
4# Télécharger les chaînes source
5npx @intlpullhq/cli upload --source locales/en.json
6
7# Les traducteurs traduisent dans l'interface web
8
9# Télécharger les traductions
10npx @intlpullhq/cli download
11# Crée locales/es.json, locales/fr.json, etc.

Les traducteurs travaillent dans une interface web, vous tirez simplement les fichiers mis à jour.

Niveau 4 : Intégration CMS (Sites Marketing)

Quand l'utiliser : Sites riches en contenu (blogs, pages marketing).

Options CMS : Contentful, Sanity, Strapi.

Comment ça marche

Au lieu de fichiers JSON, le contenu vit dans un CMS. Les éditeurs traduisent là-bas.

Exemple : Next.js + Contentful

Schéma :

JavaScript
1// Type de contenu Contentful
2{
3  "name": "Blog Post",
4  "fields": [
5    { "id": "title", "type": "Text", "localized": true },
6    { "id": "body", "type": "RichText", "localized": true },
7    { "id": "slug", "type": "Text", "localized": true }
8  ]
9}

Récupération :

TypeScript
1import { createClient } from 'contentful';
2
3const client = createClient({
4  space: process.env.CONTENTFUL_SPACE_ID,
5  accessToken: process.env.CONTENTFUL_ACCESS_TOKEN
6});
7
8export async function getBlogPost(slug: string, locale: string) {
9  const entries = await client.getEntries({
10    content_type: 'blogPost',
11    'fields.slug': slug,
12    locale: locale // 'en-US', 'es', 'fr'
13  });
14
15  return entries.items[0];
16}

Composant :

TSX
1export default async function BlogPost({ params }) {
2  const { slug, locale } = params;
3  const post = await getBlogPost(slug, locale);
4
5  return (
6    <article>
7      <h1>{post.fields.title}</h1>
8      <div>{documentToReactComponents(post.fields.body)}</div>
9    </article>
10  );
11}

Routes :

  • /en/blog/hello-world
  • /es/blog/hola-mundo
  • /fr/blog/bonjour-monde

Avantages

  • Les éditeurs non techniques peuvent traduire
  • Texte enrichi, images, champs SEO tous traduits
  • Aperçu avant publication

Inconvénients

  • Le CMS coûte de l'argent
  • Verrouillage vendeur
  • Configuration plus complexe

Niveau 5 : Plateforme i18n complète (Échelle Production)

Quand l'utiliser : Produits SaaS, sites à fort trafic, mises à jour fréquentes.

Plateformes : IntlPull, Lokalise, Phrase, Crowdin.

Ce que vous obtenez

  • UI Web pour les traducteurs (avec contexte, captures d'écran)
  • CLI pour les développeurs (upload/download des traductions)
  • Intégration Git (synchronisation bidirectionnelle)
  • Traduction automatique (DeepL, ChatGPT)
  • Mises à jour Over-the-air (changer les traductions sans déployer)
  • Mémoire de traduction (réutiliser les traductions passées)
  • Flux de travail de révision (approuver/rejeter)

Exemple : Flux de travail IntlPull

1. Configuration initiale :

Terminal
npm install @intlpullhq/react
npx @intlpullhq/cli init

2. Le développeur ajoute des chaînes dans le code :

TSX
1import { useIntlPull } from '@intlpullhq/react';
2
3function App() {
4  const { t } = useIntlPull();
5
6  return (
7    <div>
8      <h1>{t('welcome.title')}</h1>
9      <button>{t('welcome.cta')}</button>
10    </div>
11  );
12}

3. Extraire et télécharger :

Terminal
npx @intlpullhq/cli upload
# Scanne le code pour les appels t()
# Télécharge les nouvelles chaînes sur la plateforme

4. Les traducteurs traduisent (dans l'interface web, avec captures d'écran et contexte)

5. Télécharger les traductions :

Terminal
npx @intlpullhq/cli download
# Télécharge toutes les traductions
# Génère les fichiers de locale

6. Déployer :

Terminal
npm run build

7. Mises à jour OTA (optionnel) :

Plus tard, vous corrigez une coquille en espagnol. Au lieu de redéployer :

Terminal
npx @intlpullhq/cli publish --ota

Les utilisateurs reçoivent les traductions mises à jour instantanément.

Configuration Production

Next.js + IntlPull :

TypeScript
1// intlpull.config.ts
2export default {
3  projectId: 'your-project-id',
4  sourceLanguage: 'en',
5  targetLanguages: ['es', 'fr', 'de', 'ja', 'zh'],
6  ota: {
7    enabled: true,
8    pollingInterval: 60000 // Vérifier les mises à jour chaque minute
9  },
10  translation: {
11    defaultEngine: 'deepl',
12    fallback: 'google'
13  }
14};

Composant :

TSX
1'use client';
2
3import { IntlPullProvider, useIntlPull } from '@intlpullhq/react';
4
5export default function RootLayout({ children }) {
6  return (
7    <IntlPullProvider config={{ projectId: 'your-project-id' }}>
8      {children}
9    </IntlPullProvider>
10  );
11}

Avancé : Mémoire de traduction

Réutilisez les traductions passées automatiquement :

Terminal
1# Chaîne dans l'app 1 : "Save changes"
2# Traduit en espagnol : "Guardar cambios"
3
4# Plus tard, dans l'app 2, vous ajoutez "Save changes"
5# IntlPull suggère "Guardar cambios" (correspondance à 100 %)

Gagne du temps, assure la cohérence.

SEO pour les sites traduits

1. Balises hreflang

Dites à Google dans quelle langue est chaque page :

HTML
1<link rel="alternate" hreflang="en" href="https://example.com/en/page" />
2<link rel="alternate" hreflang="es" href="https://example.com/es/page" />
3<link rel="alternate" hreflang="fr" href="https://example.com/fr/page" />
4<link rel="alternate" hreflang="x-default" href="https://example.com/en/page" />

2. URLs localisées

Option A : Sous-répertoires (recommandé)

  • example.com/en/
  • example.com/es/
  • example.com/fr/

Option B : Sous-domaines

  • en.example.com
  • es.example.com
  • fr.example.com

Option C : Domaines différents

  • example.com (Anglais)
  • example.es (Espagnol)
  • example.fr (Français)

Les sous-répertoires sont les plus faciles pour le SEO (toute l'autorité dans un seul domaine).

3. Métadonnées localisées

TSX
1// Exemple Next.js
2export async function generateMetadata({ params }) {
3  const { locale } = params;
4  const titles = {
5    en: 'Best Translation Platform for Developers',
6    es: 'Mejor Plataforma de Traducción para Desarrolladores',
7    fr: 'Meilleure Plateforme de Traduction pour Développeurs'
8  };
9
10  return {
11    title: titles[locale],
12    description: t('meta.description'), // Depuis le fichier de traduction
13    openGraph: {
14      title: titles[locale],
15      locale: locale
16    }
17  };
18}

Optimisation des performances

1. Code Splitting

Chargez uniquement la langue actuelle :

JavaScript
1// ❌ Mauvais : Charger toutes les langues
2import en from './locales/en.json';
3import es from './locales/es.json';
4import fr from './locales/fr.json';
5
6// ✅ Bon : Chargement paresseux
7const loadTranslations = async (locale) => {
8  return await import(`./locales/${locale}.json`);
9};

2. Mise en cache

TypeScript
1// Mettre en cache les traductions dans localStorage
2const cacheTranslations = (locale, data) => {
3  const cache = {
4    locale,
5    data,
6    timestamp: Date.now()
7  };
8  localStorage.setItem('translations', JSON.stringify(cache));
9};
Tags
web-translation
i18n
react
nextjs
vue
website-localization
tutorial
IntlPull Team
IntlPull Team
Engineering

Building tools to help teams ship products globally. Follow us for more insights on localization and i18n.