GettextTs (gettext_ts v0.2.0)

Copy Markdown View Source

Gettext catalogs, generated to TypeScript.

For applications that split into an Elixir backend and a TypeScript frontend and want ONE translation source of truth: the Gettext PO files. The backend translates with Gettext as usual; mix gettext_ts.codegen emits typed, per-locale TypeScript catalogs the frontend consumes with a tiny t() runtime — no i18n framework, no parallel JSON catalogs, no drift.

Extracted from the Vulcora apps (mosis pioneered the pattern), with the original's known defects fixed:

  • Catalogs are nested locale → domain → msgid — the same msgid in two domains no longer collides.
  • One TypeScript file per locale AND domain, plus a lazy loadCatalog/2 index — a route tree loads the locale it needs and, within it, only the domains it renders.
  • One interpolation dialect throughout: %{var} (Gettext's).

The loop

mix gettext.extract --merge   # backend msgids (dgettext/t-macro calls)
mix gettext_ts.extract        # frontend t("...") msgids → POT + merge
# ...translate the PO files however you like...
mix gettext_ts.codegen        # PO → TypeScript

With Ash, add GettextTs.AshExtension to a domain's extensions and mix ash.codegen runs the codegen automatically.

Configuration

config :gettext_ts,
  gettext_path: "priv/gettext",           # PO/POT root
  output_path: "assets/js/i18n",          # where TS lands
  source_locale: "en",                    # msgid == msgstr identity locale
  default_domain: "default",              # frontend t()'s domain
  frontend_globs: ["assets/js/**/*.{ts,tsx}"],
  extract_function: "t",
  domain_hook: "useT",                    # `const t = useT("admin")`
  extract_ignore: :defaults,              # or a list of regexes/prefixes
  react: true                             # also emit react.tsx

All keys can be overridden per invocation with task switches.