Handlebars

Lokalisieren Sie Handlebars-Templates mit Lingo.dev CLI

Was ist Handlebars?

Handlebars ist eine beliebte Template-Engine, die die notwendige Leistung bietet, um semantische Templates effektiv zu erstellen. Sie verwendet eine klare Syntax und kompiliert Templates in JavaScript-Funktionen, wodurch sie sowohl in clientseitigen als auch in serverseitigen Anwendungen weit verbreitet zur HTML-Generierung eingesetzt wird.

Was ist Lingo.dev CLI?

Lingo.dev CLI ist eine kostenlose, quelloffene CLI zur Übersetzung von Apps und Inhalten mit KI. Sie wurde entwickelt, um traditionelle Translation-Management-Software zu ersetzen und sich gleichzeitig in bestehende Pipelines zu integrieren.

Weitere Informationen finden Sie unter Übersicht.

Über diesen Leitfaden

Dieser Leitfaden erklärt, wie Sie Handlebars-Templates mit Lingo.dev CLI lokalisieren.

Sie lernen:

  • Wie Sie Übersetzungsdateien für Handlebars-Projekte strukturieren
  • Wie Sie eine Übersetzungs-Pipeline konfigurieren
  • Wie Sie Übersetzungen mit KI generieren

Voraussetzungen

Um Lingo.dev CLI zu verwenden, stellen Sie sicher, dass Node.js v18+ installiert ist:

❯ node -v
v22.17.0

Der Handlebars-Lokalisierungsansatz

Handlebars-Templates sollten auf übersetzbare Inhalte aus JSON-Dateien verweisen, anstatt fest codierten Text zu enthalten. Dieser Ansatz bietet:

  • Klare Trennung: Template-Struktur vs. übersetzbare Inhalte
  • Versionskontrolle: Übersetzungen werden in JSON-Dateien nachverfolgt
  • Keine Mehrdeutigkeit: Explizite Definition dessen, was übersetzbar ist

Um auf Übersetzungen in Templates zuzugreifen, benötigen Sie eine Übersetzungs-Hilfsfunktion. Gängige Optionen sind:

Einfacher benutzerdefinierter Helper:

{{t "product.title"}}
{{t "greeting" name="John"}}

handlebars-i18n - Funktionsreich mit Formatierung:

{{__ "product.title"}}
{{_date releaseDate}}
{{_price amount "USD"}}

npm | GitHub

handlebars-i18next - Alternative i18next-Integration:

{{t "product.title"}}

npm | GitHub

Dieser Leitfaden verwendet {{t}} in den Beispielen, aber der Workflow gilt für jede Helper-Wahl.

Schritt 1. Projekt einrichten

Erstellen Sie im Verzeichnis Ihres Projekts eine i18n.jsonDatei:

{
  "$schema": "https://lingo.dev/schema/i18n.json",
  "version": "1.10",
  "locale": {
    "source": "en",
    "targets": ["es"]
  },
  "buckets": {}
}

Diese Datei definiert das Verhalten der Übersetzungs-Pipeline, einschließlich der Sprachen, zwischen denen übersetzt werden soll, und wo sich die lokalisierbaren Inhalte im Dateisystem befinden.

Um mehr über die verfügbaren Eigenschaften zu erfahren, siehe i18n.json.

Schritt 2. Source-Locale konfigurieren

Die Source-Locale ist die ursprüngliche Sprache und Region, in der Ihr Inhalt verfasst wurde. Um die Source-Locale zu konfigurieren, setzen Sie die Eigenschaft locale.source in der Datei i18n.json:

{
  "$schema": "https://lingo.dev/schema/i18n.json",
  "version": "1.10",
  "locale": {
    "source": "en",
    "targets": ["es"]
  },
  "buckets": {}
}

Die Source-Locale muss als BCP 47 Language Tag angegeben werden.

Für die vollständige Liste der Locale-Codes, die Lingo.dev CLI unterstützt, siehe Unterstützte Locale-Codes.

Schritt 3. Target-Locales konfigurieren

Die Target-Locales sind die Sprachen und Regionen, in die Sie Ihren Inhalt übersetzen möchten. Um die Target-Locales zu konfigurieren, setzen Sie die Eigenschaft locale.targets in der Datei i18n.json:

{
  "$schema": "https://lingo.dev/schema/i18n.json",
  "version": "1.10",
  "locale": {
    "source": "en",
    "targets": ["es"]
  },
  "buckets": {}
}

Schritt 4. Source-Content erstellen

Erstellen Sie JSON-Dateien mit Ihrem übersetzbaren Inhalt. Diese Dateien sollten in einer Verzeichnisstruktur organisiert sein, die den Source-Locale-Code enthält:

project/
├── locales/
│   └── en/
│       ├── common.json
│       └── store.json
├── templates/
│   └── product.handlebars
└── i18n.json

Das Verzeichnis es/ und die übersetzten Dateien werden automatisch von Lingo.dev CLI erstellt, wenn Sie in Schritt 8 Übersetzungen generieren.

Beispiel-JSON-Dateien

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"
  }
}

Schritt 5. Bucket erstellen

  1. Fügen Sie in der i18n.json Datei ein "json" Objekt zum buckets Objekt hinzu:

    {
      "$schema": "https://lingo.dev/schema/i18n.json",
      "version": "1.10",
      "locale": {
        "source": "en",
        "targets": ["es"]
      },
      "buckets": {
        "json": {}
      }
    }
    
  2. Definieren Sie im "json" Objekt ein Array mit einem oder mehreren include Mustern:

    {
      "$schema": "https://lingo.dev/schema/i18n.json",
      "version": "1.10",
      "locale": {
        "source": "en",
        "targets": ["es"]
      },
      "buckets": {
        "json": {
          "include": ["./locales/[locale]/*.json"]
        }
      }
    }
    

    Diese Muster definieren, welche Dateien übersetzt werden sollen.

    Die Muster selbst:

    • müssen [locale] als Platzhalter für die konfigurierte Locale enthalten
    • können auf Dateipfade verweisen (z. B. "locales/[locale]/common.json")
    • können Sternchen als Wildcard-Platzhalter verwenden (z. B. "locales/[locale]/*.json")

    Rekursive Glob-Muster (z. B. **/*.json) werden nicht unterstützt.

Schritt 6. Übersetzungen in Templates verwenden

Referenzieren Sie Übersetzungsschlüssel in Ihren Handlebars-Templates mithilfe Ihres gewählten Helpers:

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>

Wie Sie diese Übersetzungen laden und Ihre Templates kompilieren, hängt von Ihrem Build-Setup und der gewählten Helper-Bibliothek ab.

Schritt 7. LLM konfigurieren

Die Lingo.dev CLI verwendet Large Language Models (LLMs), um Inhalte mit KI zu übersetzen. Um eines dieser Modelle zu verwenden, benötigen Sie einen API-Schlüssel von einem unterstützten Anbieter.

Um so schnell wie möglich loszulegen, empfehlen wir die Verwendung der Lingo.dev Engine:

  1. Registrieren Sie sich für ein Lingo.dev-Konto.

  2. Führen Sie den folgenden Befehl aus:

    npx lingo.dev@latest login
    

    Dies öffnet Ihren Standard-Browser und fordert Sie zur Authentifizierung auf.

  3. Folgen Sie den Anweisungen.

Schritt 8. Übersetzungen generieren

Führen Sie im Verzeichnis, das die i18n.json Datei enthält, den folgenden Befehl aus:

npx lingo.dev@latest run

Dieser Befehl:

  1. Liest die i18n.json Datei.
  2. Findet die JSON-Dateien, die übersetzt werden müssen.
  3. Extrahiert den übersetzbaren Inhalt aus den Dateien.
  4. Verwendet das konfigurierte LLM, um den extrahierten Inhalt zu übersetzen.
  5. Schreibt den übersetzten Inhalt zurück in das Dateisystem.

Beim ersten Mal, wenn Übersetzungen generiert werden, wird eine i18n.lock Datei erstellt. Diese Datei verfolgt, welche Inhalte übersetzt wurden, und verhindert unnötige erneute Übersetzungen bei nachfolgenden Durchläufen.

Beispiel

Projektstruktur

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