Internationaliser une mini‑boutique
Un tutoriel React pratique qui internationalise une boutique simple à l’aide des composants React de GT, des hooks et des chaînes partagées
Faites tourner une petite « mini‑boutique » entièrement locale et traduite — sans services externes, sans routage, sans frameworks UI. Vous utiliserez de bout en bout les fonctionnalités React de GT 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 « boutique » monopage avec une grille de produits et un panier en mémoire simple
- Sélecteur de langue et libellés de navigation partagés
- Nombres, devise et 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, Changer de langue
Installez et enveloppez 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 dans les 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"]}> {/* Active l'espagnol et le français */}
<App />
</GTProvider>
</StrictMode>
);Vous pouvez éventuellement ajouter un gt.config.json dès maintenant (utile plus tard pour la CI 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 utilisée). Pour afficher 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’application
Nous allons coder en dur un petit tableau de produits et tout conserver 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 circum-auriculaire à réduction de bruit avec autonomie de 30h',
price: 199.99,
currency: 'USD',
inStock: true,
addedAt: new Date().toISOString()
},
{
id: 'p-2',
name: 'Mug isotherme',
description: 'Acier inoxydable à double paroi isolante (355 ml)',
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écoder ces chaînes via 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 Devise
Utilisez <T> pour la traduction JSX. Encapsulez le contenu dynamique à l’aide de 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é le : <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 gestion du pluriel 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 espaces réservés des 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 }}
/>
);
}Assembler le tout
Une application monopage avec panier en mémoire et filtre de recherche simple.
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 via<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 des branches, Guide des chaînes, Changer de langue
Comment trouvez-vous ce guide ?