Quick start#

This example generates runtime translations for English, German, and French, plus TypeScript and Rust bindings. Every file shown here belongs to the runnable fixture under docs/examples/quickstart.

1. Write the catalog#

Create translations.toml:

translations.toml
[account.greeting]
en = "Welcome back, {{name}}!"
de = "Willkommen zurück, {{name}}!"
fr = "Bon retour, {{name}} !"
arguments = { name = "string" }

[cart.summary]
en = "You have {{count}} items in your cart."
de = "Du hast {{count}} Artikel in deinem Warenkorb."
fr = "Vous avez {{count}} articles dans votre panier."
arguments = { count = "number" }

[navigation.sign_out]
en = "Sign out"
de = "Abmelden"
fr = "Se déconnecter"

The table path is the translation key. Language fields hold the text, and arguments declares the placeholders accepted by the Handlebars template.

2. Configure the outputs#

Create globetrotter.yaml beside it:

globetrotter.yaml
version: 1
configs:
  app:
    languages: [en, de, fr]
    engine: handlebars
    strict: true
    check_templates: true
    inputs:
      - path: ./translations.toml
        prefix: app
    outputs:
      json:
        - ./generated/translations_{{language}}.json
      typescript:
        type: ./generated/translations.ts
      rust:
        - ./generated/translations.rs

The config:

  • enables English, German, and French;
  • checks Handlebars templates while generating;
  • prefixes every key with app;
  • writes one JSON file per language;
  • writes TypeScript and Rust bindings once.

3. Generate#

Run the CLI from the directory containing the config:

$ globetrotter -c globetrotter.yaml
 
   app[rust]       wrote generated/translations.rs
   app[typescript] wrote generated/translations.ts
   app[de]         wrote generated/translations_de.json (354 B, 227 B gzipped)
   app[en]         wrote generated/translations_en.json (340 B, 206 B gzipped)
   app[fr]         wrote generated/translations_fr.json (355 B, 225 B gzipped)
                   completed in 0.00ms

With no --config flag, globetrotter discovers globetrotter.yaml in the current directory. Passing -c globetrotter.yaml makes the example explicit.

4. Inspect a runtime file#

The English JSON output is generated from the catalog above:

translations_en.jsongenerated
{
  "version": "1",
  "template_engine": "handlebars",
  "language": "en",
  "translations": {
    "app.account.greeting": {
      "template": "Welcome back, {{name}}!"
    },
    "app.cart.summary": {
      "template": "You have {{count}} items in your cart."
    },
    "app.navigation.sign_out": {
      "literal": "Sign out"
    }
  }
}

The German and French files have the same keys, which lets application code select a language at runtime without changing its lookup contract.

5. Inspect the bindings#

The same run writes TypeScript:

translations.tsgenerated
//
// AUTOGENERATED. DO NOT EDIT.
// generated by globetrotter v0.0.12.
//
export type Translations = {
    readonly "app.account.greeting": (values: {
        readonly "name": string;
    }) => string;
    readonly "app.cart.summary": (values: {
        readonly "count": number;
    }) => string;
    readonly "app.navigation.sign_out": string;
};

and Rust:

translations.rsgenerated
//
// AUTOGENERATED. DO NOT EDIT.
// generated by globetrotter v0.0.12.
//

#[derive(
    Debug,
    Clone,
    PartialEq,
    Eq,
    PartialOrd,
    Ord,
    ::serde::Serialize,
    ::serde::Deserialize,
)]
#[serde(untagged)]
pub enum Translation<'a> {
    AppAccountGreeting { #[serde(rename = "name")] name: &'a str },
    AppCartSummary { #[serde(rename = "count")] count: i64 },
    AppNavigationSignOut {},
}
impl<'a> Translation<'a> {
    pub fn key(&self) -> &'static str {
        match self {
            Self::AppAccountGreeting { .. } => "app.account.greeting",
            Self::AppCartSummary { .. } => "app.cart.summary",
            Self::AppNavigationSignOut { .. } => "app.navigation.sign_out",
        }
    }
}

These are generated files; do not edit them by hand. Change the TOML catalog or YAML config and run Globetrotter again.

6. Lint the sources#

globetrotter lint
$ globetrotter lint
 
 
 INFO globetrotter::lint: no issues found in 0ms

The lint command reads the same discovered config but never writes output. Add it to CI alongside a check that committed generated files are current.

Next: configuration and translation files.