Skip to content

How to serve locale-specific data ​

You want the same generator definition to produce Turkish cities under one locale and American cities under another — without branching in the schema. In Phony the generator is fixed; only the active locale chain changes, and the asset it reads resolves to different data per locale.

How resolution works ​

An asset is a name (like demo.cities) mapped to per-locale data. When a generator reads it, Phony walks the active chain most-specific first, and if no entry matches, falls back to the locale-independent * entry:

text
domain  >  locale  >  base (*)

So a chain of ["tr_TR"] tries tr_TR, then *. A chain of ["fr_FR","en_US"] tries fr_FR, then en_US, then *. The * fallback is always tried last, even when you didn't put it in the chain. Adding a locale to an asset never affects how any other locale resolves.

Steps ​

  1. In a package's assets block, map an asset name to { locale → data }, including a * fallback.
  2. Write a generator that reads the asset by name ({ "type": "list", "source": "<asset>" }).
  3. Run phony generate with --locale, a comma-separated chain, most-specific first.

Complete example ​

locale-pkg/phony.json — one generator, one asset, three locales:

json
{
  "name": "@demo/geo",
  "version": "0.1.0",
  "generators": [
    { "name": "@demo/geo:city", "body": "{{ city }}",
      "let": { "city": { "type": "list", "source": "demo.cities" } } }
  ],
  "assets": {
    "lists": {
      "demo.cities": {
        "tr_TR": ["İstanbul", "Ankara", "İzmir", "Bursa"],
        "en_US": ["New York", "Chicago", "Denver", "Austin"],
        "*":     ["Metropolis", "Gotham", "Springfield"]
      }
    }
  }
}

The same command, two locales:

bash
phony generate --use @demo/geo:city --package ./locale-pkg --locale tr_TR --seed 42 -n 4 -f jsonl
json
"Bursa"
"Ankara"
"Ankara"
"Ankara"
bash
phony generate --use @demo/geo:city --package ./locale-pkg --locale en_US --seed 42 -n 4 -f jsonl
json
"Austin"
"Chicago"
"Chicago"
"Chicago"

Byte-for-byte the same definition and the same seed — only the chain changed, so only the resolved data differs.

Fallback in action ​

Omit --locale entirely and the chain is just *:

bash
phony generate --use @demo/geo:city --package ./locale-pkg --seed 42 -n 4 -f jsonl
json
"Metropolis"
"Metropolis"
"Gotham"
"Springfield"

Pass a chain whose most-specific locale is missing, and resolution walks down it. demo.cities has no fr_FR, so fr_FR,en_US resolves through to the US data:

bash
phony generate --use @demo/geo:city --package ./locale-pkg --locale fr_FR,en_US --seed 42 -n 4 -f jsonl
json
"Austin"
"Chicago"
"Chicago"
"Chicago"

Contributing a locale to someone else's asset ​

Because assets merge by (asset, locale), a package can add a locale to an asset another package already defines — without either touching the other. A data-only package that adds Azerbaijani cities to demo.cities:

json
{
  "name": "@aziz/az-cities",
  "version": "0.1.0",
  "assets": {
    "lists": {
      "demo.cities": {
        "az_AZ": ["Bakı", "Gəncə", "Sumqayıt", "Mingəçevir"]
      }
    }
  }
}

Load both packages (--package ./locale-pkg --package ./az-cities) and --locale az_AZ now resolves to the contributed list. Later --package directories layer on top of earlier ones.

Models are locale-polymorphic too ​

The same shape works for trained N-gram models under assets.models — a { "type": "model", "source": "person.first_names" } generator can resolve a Turkish model under tr_TR and an English one under en_US, invented novel names instead of picking from a fixed list. Train a model with phony train (see the CLI reference) and point each locale at its .ngram file:

json
{ "assets": { "models": { "person.first_names": {
  "tr_TR": "models/tr_first_names.ngram",
  "en_US": "models/en_first_names.ngram"
} } } }

Pitfalls ​

  • * is always the last resort, tried after every locale in the chain — so give every asset a sensible * entry, or a lookup with no matching locale returns nothing and the generator errors.
  • The chain is most-specific first. --locale en_US,tr_TR prefers English and only falls back to Turkish; reverse it to prefer Turkish.
  • A "domain" is just a more-specific chain entry (e.g. put a domain tag ahead of the locale). There's no separate domain flag — it's the same precedence list.
  • Locale tags are opaque strings. tr_TR and tr are different keys; be consistent between your asset keys and your --locale chain.

See also ​

Phony Cloud — Documentation & Specification