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:
[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:
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.rsThe 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:
{
"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:
//
// 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:
//
// 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 0msThe 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.