Internationaliser une mini-boutique
Un tutoriel React pratique qui internationalise une boutique simple à l'aide des composants, hooks et chaînes partagées de GT React
Lancez une petite « mini-boutique » entièrement locale et traduite — sans services externes, sans routage, sans frameworks d’UI. Vous utiliserez de bout en bout les fonctionnalités essentielles de GT React et verrez comment elles s’assemblent dans une interface simple et réaliste.
Prérequis : React, bases de JavaScript/TypeScript
Ce que vous allez construire
- Une page « boutique » avec une grille de produits et un panier en mémoire simple
- Un sélecteur de langue et des libellés de navigation partagés
- Des nombres, des devises et une pluralisation correctement internationalisés
- Optionnel : stockage local des traductions pour les builds de production
Liens utilisés dans ce tutoriel
- Provider : <GTProvider>
- Composants : <T>,<Var>,<Num>,<Currency>,<DateTime>,<Branch>,<Plural>,<LocaleSelector>
- Chaînes et chaînes partagées : useGT,msg,useMessages
- Guides : Variables, Branches, Chaînes, Stockage local des traductions, Changement de langue
Installez et intégrez votre application
Installez les packages et enveloppez votre application avec le provider.
npm i gt-react
npm i --save-dev gtx-cliyarn add gt-react
yarn add --dev gtx-clibun add gt-react
bun add --dev gtx-clipnpm add gt-react
pnpm add --save-dev gtx-cliOptionnel : projet de démarrage (Vite)
Si vous partez de zéro, générez une application Vite React + TypeScript, puis installez les packages GT :
npm create vite@latest mini-shop -- --template react-ts
cd mini-shop
npm i gt-react
npm i --save-dev gtx-cliAjoutez ensuite les fichiers des sections ci‑dessous (p. ex. src/main.tsx, src/App.tsx, src/components/*, src/data.ts, src/nav.ts).
Créez une configuration minimale du provider.
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { GTProvider } from 'gt-react'; // Voir : /docs/react/api/components/gtprovider
import App from './App';
createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <GTProvider locales={["es", "fr"]}> {/* Activer l'espagnol et le français */}
      <App />
    </GTProvider>
  </StrictMode>
);Ajoutez éventuellement un fichier gt.config.json maintenant (utile plus tard pour l’intégration continue et le stockage local) :
{
  "defaultLocale": "en",
  "locales": ["es", "fr"]
}Clés d’API de développement
Vous pouvez suivre ce tutoriel sans clés (la langue par défaut sera affichée). Pour voir les traductions en temps réel et tester le changement de langue en développement, ajoutez des clés de développement.
En savoir plus dans Production vs Development.
VITE_GT_API_KEY="your-dev-api-key"
VITE_GT_PROJECT_ID="your-project-id"REACT_APP_GT_API_KEY="your-dev-api-key"
REACT_APP_GT_PROJECT_ID="your-project-id"- Tableau de bord : https://dash.generaltranslation.com/signup
- Ou via la CLI :
npx gtx-cli auth
Données d’amorçage et structure de l’app
Nous allons coder en dur un petit tableau de produits et tout garder côté client. Pas de serveur, pas de routage.
export type Product = {
  id: string;
  name: string;
  description: string;
  price: number;
  currency: 'USD' | 'EUR';
  inStock: boolean;
  addedAt: string; // ISO date string
};
export const products: Product[] = [
  {
    id: 'p-1',
    name: 'Casque sans fil',
    description: 'Design supra-auriculaire à réduction de bruit avec batterie 30h',
    price: 199.99,
    currency: 'USD',
    inStock: true,
    addedAt: new Date().toISOString()
  },
  {
    id: 'p-2',
    name: 'Mug de voyage',
    description: 'Acier inoxydable à double paroi isolante (12oz)',
    price: 24.5,
    currency: 'USD',
    inStock: false,
    addedAt: new Date().toISOString()
  }
];Libellés de navigation partagés avec msg et useMessages
Utilisez msg pour baliser des chaînes partagées dans la configuration, puis décodez-les avec useMessages au moment du rendu.
import { msg } from 'gt-react'; // Voir : /docs/react/api/strings/msg
export const nav = [
  { label: msg('Accueil'), href: '#' },
  { label: msg('Produits'), href: '#products' },
  { label: msg('Panier'), href: '#cart' }
];import { LocaleSelector, T } from 'gt-react';
import { useMessages } from 'gt-react'; // Voir : /docs/react/api/strings/useMessages
import { nav } from '../nav';
export default function Header() {
  const m = useMessages();
  return (
    <header style={{ display: 'flex', gap: 16, alignItems: 'center' }}>
      <T><h1>Mini Shop</h1></T> {/* Voir : /docs/react/api/components/t */}
      <nav style={{ display: 'flex', gap: 12 }}>
        {nav.map(item => (
          <a key={item.href} href={item.href} title={m(item.label)}>
            {m(item.label)}
          </a>
        ))}
      </nav>
      <div style={{ marginLeft: 'auto' }}>
        <LocaleSelector /> {/* Voir : /docs/react/api/components/localeSelector */}
      </div>
    </header>
  );
}Fiches produit avec <T>, Variables, Branch et Currency
Utilisez <T> pour la traduction JSX. Enveloppez le contenu dynamique avec des composants de variables comme <Var>, <Num>, <Currency> et <DateTime>. Gérez l’état des stocks via <Branch>.
import { T, Var, Num, Currency, DateTime, Branch } from 'gt-react';
import type { Product } from '../data';
export default function ProductCard({ product, onAdd }: { product: Product; onAdd: () => void; }) {
  return (
    <div style={{ border: '1px solid #ddd', padding: 12, borderRadius: 8 }}>
      <T>
        <h3><Var>{product.name}</Var></h3>
        <p><Var>{product.description}</Var></p>
        <p>
          Prix : <Currency currency={product.currency}>{product.price}</Currency>
        </p>
        <p>
          Ajouté : <DateTime options={{ dateStyle: 'medium', timeStyle: 'short' }}>{product.addedAt}</DateTime>
        </p>
        <Branch
          branch={product.inStock}
          true={<p>En stock</p>}
          false={<p style={{ color: 'tomato' }}>Rupture de stock</p>}
        />
        <button onClick={onAdd} disabled={!product.inStock}>
          Ajouter au panier
        </button>
      </T>
    </div>
  );
}Panier avec pluralisation et totaux
Utilisez <Plural> pour exprimer « X articles dans le panier » et <Currency> pour les totaux. À combiner avec <T>, <Var> et <Num>.
import { T, Plural, Var, Num, Currency } from 'gt-react';
import type { Product } from '../data';
export default function Cart({ items, onClear }: { items: Product[]; onClear: () => void; }) {
  const total = items.reduce((sum, p) => sum + p.price, 0);
  const itemCount = items.length;
  return (
    <div style={{ borderTop: '1px solid #eee', paddingTop: 12 }}>
      <T>
        <h2>Panier</h2>
        <Plural
          n={itemCount}
          zero={<p>Votre panier est vide</p>}
          one={<p>Vous avez <Num>{itemCount}</Num> article</p>}
          other={<p>Vous avez <Num>{itemCount}</Num> articles</p>}
        />
        {items.map((p) => (
          <p key={p.id}>
            <Var>{p.name}</Var> — <Currency currency={p.currency}>{p.price}</Currency>
          </p>
        ))}
        <p>
          Total : <Currency currency={items[0]?.currency || 'USD'}>{total}</Currency>
        </p>
        <button onClick={onClear} disabled={itemCount === 0}>Vider le panier</button>
      </T>
    </div>
  );
}Attributs et espaces réservés avec useGT
Utilisez useGT pour traduire des chaînes simples, comme les placeholders de champs de saisie et les libellés ARIA.
import { useGT } from 'gt-react';
export default function Search({ onQuery }: { onQuery: (q: string) => void; }) {
  const t = useGT();
  return (
    <input
      type="search"
      placeholder={t('Rechercher des produits...')}
      aria-label={t('Champ de recherche')}
      onChange={(e) => onQuery(e.target.value)}
      style={{ padding: 8, width: '100%', maxWidth: 320 }}
    />
  );
}Rassembler le tout
Une application monopage avec panier en mémoire et un simple filtre de recherche.
import { useMemo, useState } from 'react';
import Header from './components/Header';
import Search from './components/Search';
import ProductCard from './components/ProductCard';
import Cart from './components/Cart';
import { products } from './data';
export default function App() {
  const [query, setQuery] = useState('');
  const [cart, setCart] = useState<string[]>([]);
  const filtered = useMemo(() => {
    const q = query.toLowerCase();
    return products.filter(p =>
      p.name.toLowerCase().includes(q) || p.description.toLowerCase().includes(q)
    );
  }, [query]);
  const items = products.filter(p => cart.includes(p.id));
  return (
    <div style={{ margin: '24px auto', maxWidth: 960, padding: 16 }}>
      <Header />
      <div style={{ margin: '16px 0' }}>
        <Search onQuery={setQuery} />
      </div>
      <section id="products" style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 1fr))', gap: 16 }}>
        {filtered.map(p => (
          <ProductCard
            key={p.id}
            product={p}
            onAdd={() => setCart(c => (p.inStock ? [...new Set([...c, p.id])] : c))}
          />
        ))}
      </section>
      <section id="cart" style={{ marginTop: 24 }}>
        <Cart
          items={items}
          onClear={() => setCart([])}
        />
      </section>
    </div>
  );
}Exécuter en local
Ajoutez un script de développement simple à votre package.json, puis démarrez l’application.
{
  "scripts": {
    "dev": "vite"
  }
}Exécutez :
npm run dev{
  "scripts": {
    "start": "react-scripts start"
  }
}Exécutez :
npm startCe que vous avez appris
- Traduire du JSX avec <T>et gérer le contenu dynamique avec<Var>,<Num>,<Currency>,<DateTime>
- Exprimer du contenu conditionnel avec <Branch>et gérer les quantités avec<Plural>
- Traduire des attributs avec useGT
- Partager des chaînes de navigation/de configuration avec msget les décoder avecuseMessages
- Changer de langue avec <LocaleSelector>
Prochaines étapes
- À explorer : Guide des variables, Guide du branchement, Guide des chaînes, Changer de langue
Que pensez-vous de ce guide ?

