Handlebars
Localisez les templates Handlebars avec Lingo.dev CLI
Qu'est-ce que Handlebars ?
Handlebars est un moteur de templates populaire qui fournit la puissance nécessaire pour créer efficacement des templates sémantiques. Il utilise une syntaxe claire et compile les templates en fonctions JavaScript, ce qui le rend largement utilisé pour générer du HTML dans les applications côté client et côté serveur.
Qu'est-ce que Lingo.dev CLI ?
Lingo.dev CLI est un CLI gratuit et open source pour traduire des applications et du contenu avec l'IA. Il est conçu pour remplacer les logiciels de gestion de traduction traditionnels tout en s'intégrant aux pipelines existants.
Pour en savoir plus, consultez Présentation.
À propos de ce guide
Ce guide explique comment localiser les templates Handlebars en utilisant Lingo.dev CLI.
Vous apprendrez à :
- Structurer les fichiers de traduction pour les projets Handlebars
- Configurer un pipeline de traduction
- Générer des traductions avec l'IA
Prérequis
Pour utiliser Lingo.dev CLI, assurez-vous que Node.js v18+ est installé :
❯ node -v
v22.17.0
L'approche de localisation Handlebars
Les templates Handlebars doivent référencer le contenu traduisible à partir de fichiers JSON plutôt que de contenir du texte codé en dur. Cette approche offre :
- Séparation claire : structure du template vs contenu traduisible
- Contrôle de version : traductions suivies dans les fichiers JSON
- Aucune ambiguïté : définir explicitement ce qui est traduisible
Pour accéder aux traductions dans les templates, vous aurez besoin d'une fonction helper de traduction. Les options courantes incluent :
Helper personnalisé simple :
{{t "product.title"}}
{{t "greeting" name="John"}}
handlebars-i18n - Riche en fonctionnalités avec formatage :
{{__ "product.title"}}
{{_date releaseDate}}
{{_price amount "USD"}}
handlebars-i18next - Intégration alternative i18next :
{{t "product.title"}}
Ce guide utilise {{t}} dans les exemples, mais le workflow s'applique à n'importe quel choix d'helper.
Étape 1. Configurer un projet
Dans le répertoire de votre projet, créez un fichier i18n.json :
{
"$schema": "https://lingo.dev/schema/i18n.json",
"version": "1.10",
"locale": {
"source": "en",
"targets": ["es"]
},
"buckets": {}
}
Ce fichier définit le comportement du pipeline de traduction, y compris les langues entre lesquelles traduire et l'emplacement du contenu localisable sur le système de fichiers.
Pour en savoir plus sur les propriétés disponibles, consultez i18n.json.
Étape 2. Configurer la locale source
La locale source est la langue et la région d'origine dans lesquelles votre contenu a été rédigé. Pour configurer la locale source, définissez la propriété locale.source dans le fichier i18n.json :
{
"$schema": "https://lingo.dev/schema/i18n.json",
"version": "1.10",
"locale": {
"source": "en",
"targets": ["es"]
},
"buckets": {}
}
La locale source doit être fournie sous forme de balise de langue BCP 47.
Pour la liste complète des codes de locale pris en charge par Lingo.dev CLI, consultez Codes de locale pris en charge.
Étape 3. Configurer les locales cibles
Les locales cibles sont les langues et régions dans lesquelles vous souhaitez traduire votre contenu. Pour configurer les locales cibles, définissez la propriété locale.targets dans le fichier i18n.json :
{
"$schema": "https://lingo.dev/schema/i18n.json",
"version": "1.10",
"locale": {
"source": "en",
"targets": ["es"]
},
"buckets": {}
}
Étape 4. Créer le contenu source
Créez des fichiers JSON contenant votre contenu traduisible. Ces fichiers doivent être organisés dans une structure de répertoires qui inclut le code de la locale source :
project/
├── locales/
│ └── en/
│ ├── common.json
│ └── store.json
├── templates/
│ └── product.handlebars
└── i18n.json
Le répertoire es/ et les fichiers traduits seront créés automatiquement par Lingo.dev CLI lorsque vous générerez les traductions à l'étape 8.
Exemples de fichiers JSON
locales/en/common.json :
{
"navigation": {
"home": "Home",
"products": "Products",
"about": "About",
"contact": "Contact"
},
"footer": {
"copyright": "All rights reserved",
"privacy": "Privacy Policy",
"terms": "Terms of Service"
}
}
locales/en/store.json :
{
"product": {
"title": "Wireless Headphones",
"description": "Premium sound quality with active noise cancellation",
"price": "Price",
"inStock": "In Stock",
"outOfStock": "Out of Stock"
},
"cart": {
"add": "Add to Cart"
},
"actions": {
"buyNow": "Buy Now"
}
}
Étape 5. Créer un bucket
-
Dans le fichier
i18n.json, ajoutez un objet"json"à l'objetbuckets:{ "$schema": "https://lingo.dev/schema/i18n.json", "version": "1.10", "locale": { "source": "en", "targets": ["es"] }, "buckets": { "json": {} } } -
Dans l'objet
"json", définissez un tableau d'un ou plusieurs motifsinclude:{ "$schema": "https://lingo.dev/schema/i18n.json", "version": "1.10", "locale": { "source": "en", "targets": ["es"] }, "buckets": { "json": { "include": ["./locales/[locale]/*.json"] } } }Ces motifs définissent quels fichiers traduire.
Les motifs eux-mêmes :
- doivent contenir
[locale]comme espace réservé pour la locale configurée - peuvent pointer vers des chemins de fichiers (par exemple,
"locales/[locale]/common.json") - peuvent utiliser des astérisques comme espaces réservés génériques (par exemple,
"locales/[locale]/*.json")
Les motifs glob récursifs (par exemple,
**/*.json) ne sont pas pris en charge. - doivent contenir
Étape 6. Utiliser les traductions dans les templates
Référencez les clés de traduction dans vos templates Handlebars en utilisant l'assistant de votre choix :
templates/product.handlebars :
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{{t "product.title"}}</title>
</head>
<body>
<nav>
<a href="/">{{t "navigation.home"}}</a>
<a href="/products">{{t "navigation.products"}}</a>
<a href="/about">{{t "navigation.about"}}</a>
<a href="/contact">{{t "navigation.contact"}}</a>
</nav>
<main>
<article>
<h1>{{t "product.title"}}</h1>
<p>{{t "product.description"}}</p>
<div class="price">
<span>{{t "product.price"}}:</span>
<span>$299.99</span>
</div>
<div class="stock">
{{#if inStock}}
<span class="available">{{t "product.inStock"}}</span>
{{else}}
<span class="unavailable">{{t "product.outOfStock"}}</span>
{{/if}}
</div>
<div class="actions">
<button class="primary">{{t "cart.add"}}</button>
<button class="secondary">{{t "actions.buyNow"}}</button>
</div>
</article>
</main>
<footer>
<p>{{t "footer.copyright"}}</p>
<a href="/privacy">{{t "footer.privacy"}}</a>
<a href="/terms">{{t "footer.terms"}}</a>
</footer>
</body>
</html>
La manière dont vous chargez ces traductions et compilez vos templates dépend de votre configuration de build et de la bibliothèque d'assistant choisie.
Étape 7. Configurer un LLM
Lingo.dev CLI utilise des modèles de langage de grande taille (LLM) pour traduire le contenu avec l'IA. Pour utiliser l'un de ces modèles, vous avez besoin d'une clé API d'un fournisseur pris en charge.
Pour démarrer le plus rapidement possible, nous recommandons d'utiliser Lingo.dev Engine :
-
Exécutez la commande suivante :
npx lingo.dev@latest loginCela ouvrira votre navigateur par défaut et vous demandera de vous authentifier.
-
Suivez les instructions.
Étape 8. Générer les traductions
Dans le répertoire qui contient le fichier i18n.json, exécutez la commande suivante :
npx lingo.dev@latest run
Cette commande :
- Lit le fichier
i18n.json. - Trouve les fichiers JSON qui doivent être traduits.
- Extrait le contenu traduisible des fichiers.
- Utilise le LLM configuré pour traduire le contenu extrait.
- Écrit le contenu traduit dans le système de fichiers.
La première fois que les traductions sont générées, un fichier i18n.lock est créé. Ce fichier garde une trace du contenu qui a été traduit, empêchant les retraductions inutiles lors des exécutions suivantes.
Exemple
Structure du projet
handlebars-localization/
├── locales/
│ ├── en/
│ │ ├── common.json
│ │ └── store.json
│ └── es/
│ ├── common.json
│ └── store.json
├── templates/
│ └── product.handlebars
└── i18n.json
locales/en/common.json
{
"navigation": {
"home": "Home",
"products": "Products",
"about": "About",
"contact": "Contact"
},
"footer": {
"copyright": "All rights reserved",
"privacy": "Privacy Policy",
"terms": "Terms of Service"
}
}
locales/en/store.json
{
"product": {
"title": "Wireless Headphones",
"description": "Premium sound quality with active noise cancellation",
"price": "Price",
"inStock": "In Stock",
"outOfStock": "Out of Stock"
},
"cart": {
"add": "Add to Cart"
},
"actions": {
"buyNow": "Buy Now"
}
}
locales/es/common.json
{
"navigation": {
"home": "Inicio",
"products": "Productos",
"about": "Acerca de",
"contact": "Contacto"
},
"footer": {
"copyright": "Todos los derechos reservados",
"privacy": "Política de privacidad",
"terms": "Términos de servicio"
}
}
locales/es/store.json
{
"product": {
"title": "Auriculares inalámbricos",
"description": "Calidad de sonido premium con cancelación activa de ruido",
"price": "Precio",
"inStock": "En stock",
"outOfStock": "Agotado"
},
"cart": {
"add": "Añadir al carrito"
},
"actions": {
"buyNow": "Comprar ahora"
}
}
templates/product.handlebars
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{{t "product.title"}}</title>
</head>
<body>
<nav>
<a href="/">{{t "navigation.home"}}</a>
<a href="/products">{{t "navigation.products"}}</a>
<a href="/about">{{t "navigation.about"}}</a>
<a href="/contact">{{t "navigation.contact"}}</a>
</nav>
<main>
<article>
<h1>{{t "product.title"}}</h1>
<p>{{t "product.description"}}</p>
<div class="price">
<span>{{t "product.price"}}:</span>
<span>$299.99</span>
</div>
<div class="stock">
{{#if inStock}}
<span class="available">{{t "product.inStock"}}</span>
{{else}}
<span class="unavailable">{{t "product.outOfStock"}}</span>
{{/if}}
</div>
<div class="actions">
<button class="primary">{{t "cart.add"}}</button>
<button class="secondary">{{t "actions.buyNow"}}</button>
</div>
</article>
</main>
<footer>
<p>{{t "footer.copyright"}}</p>
<a href="/privacy">{{t "footer.privacy"}}</a>
<a href="/terms">{{t "footer.terms"}}</a>
</footer>
</body>
</html>
i18n.json
{
"$schema": "https://lingo.dev/schema/i18n.json",
"version": "1.10",
"locale": {
"source": "en",
"targets": ["es"]
},
"buckets": {
"json": {
"include": ["./locales/[locale]/*.json"]
}
}
}
i18n.lock
version: 1
checksums:
8a4f2c9e1d6b3a7f5e8c2d1b9a3f6e4c:
navigation.home: 7b2e4f9a1c8d3b6f5e2a9d1c8b4f7e3a
navigation.products: 3f8e2a9d1c6b4f7e5a2c9d1b8f4e7a3b
navigation.about: 9d1c8b4f7e3a2f9e1d6b3a7f5e8c2d1b
navigation.contact: 4f7e3a2c9d1b8f6e5a3f2d9c1b7e4a8f
footer.copyright: 2c9d1b8f4e7a3b6f5e2a9d1c8b4f7e3a
footer.privacy: 8b4f7e3a2c9d1b6f5e2a9d1c8f4e7a3b
footer.terms: 6f5e2a9d1c8b4f7e3a2c9d1b8f4e7a3b
3b6f5e2a9d1c8b4f7e3a2c9d1b8f4e7a:
product.title: 1c8b4f7e3a2c9d1b6f5e2a9d1c8f4e7a
product.description: 7e3a2c9d1b6f5e2a9d1c8b4f7e3a2c9d
product.price: 9d1b6f5e2a9d1c8b4f7e3a2c9d1b8f4e
product.inStock: 4f7e3a2c9d1b6f5e2a9d1c8b4f7e3a2c
product.outOfStock: 2c9d1b6f5e2a9d1c8b4f7e3a2c9d1b8f
cart.add: 8b4f7e3a2c9d1b6f5e2a9d1c8b4f7e3a
actions.buyNow: 7e3a2c9d1b6f5e2a9d1c8b4f7e3a2c9d