escalated-locale is the central source of truth for Escalated translations. It consolidates locale catalogs from the Vue frontend and the framework plugins into a single canonical locales/{locale}.json set, then republishes that data through thin ecosystem-specific packages.
portfolio source repos
-> escalated-locale/locales/*.json
-> npm package (@escalated-dev/locale)
-> Composer package (escalated-dev/locale)
-> RubyGem (escalated-locale)
-> PyPI package (escalated-locale)
-> Maven package (dev.escalated:escalated-locale)
-> NuGet package (Escalated.Locale)
-> Hex package (:escalated_locale)
-> Go module (github.com/escalated-dev/escalated-locale/packages/go)
-> consumed by framework plugins
-> merged with repo-local overrides at app boot
- Update the source translation in the portfolio repo, or edit
locales/en.jsondirectly if the central package is now the canonical owner. - Run
python scripts/build_locales.py(regenerates the canonical JSON from upstream sources, if needed). - Run the sync script:
bash scripts/sync.sh(Linux/macOS/git-bash) orpwsh ./scripts/sync.ps1(Windows). Both write the same outputs — JSON copies into eachpackages/*/plus framework-native artifacts (Rails YAML, Django/WP gettext, Symfony YAML, Spring properties, Phoenix .po) viascripts/build_native_artifacts.py. - Commit the locale changes and the synced package copies.
- Tag a release when the bundle is ready to publish.
- Copy
locales/en.jsontolocales/{locale}.json. - Translate the values.
- Run
bash scripts/sync.sh(orpwsh ./scripts/sync.ps1). - Commit the new locale and synced package copies.
Each plugin should load the package locale data first, then layer its own framework-local overrides on top. The plugin remains free to keep framework-specific translations or emergency patches in its own repo; the central package provides the base catalog and fallback chain.
- Locale set:
ar,de,en,es,fr,it,ja,ko,nl,pl,pt-BR,ru,tr,zh-CN. New locales are added by copyingen.json. - Placeholder syntax:
{name}(curly-brace named tokens, e.g."Welcome {firstName}"). The wrappert()helpers interpolate these. - Key naming: lowerCamelCase with dot-separated namespaces (e.g.
ticket.status.open). - Conflict resolution (when the same key appeared in multiple source repos with divergent values): Vue frontend wins, then Laravel, Filament, Rails, the compact backend cluster, Django, and WordPress in that order.
- Gettext namespaces: Django strings live under
djangoStrings.*and WordPress strings underwordpressStrings.*, keyed deterministically from the Englishmsgid. - Missing translations: a key absent from a non-English locale falls back to the English value (no empty strings shipped).
const { getLocaleData, t } = require('@escalated-dev/locale')
const messages = getLocaleData('fr')
const label = t('ticket.subject', 'fr')use Escalated\Locale\Locale;
$messages = Locale::getLocaleData('fr');
$label = Locale::translate('ticket.subject', 'fr');require "escalated/locale"
messages = Escalated::Locale.get_locale_data("fr")
label = Escalated::Locale.t("ticket.subject", "fr")import dev.escalated.locale.Locale;
Map<String, Object> messages = Locale.getLocaleData("fr");
String label = Locale.translate("ticket.subject", "fr", Map.of());using Escalated.Locale;
var messages = LocaleData.GetLocaleData("fr");
var label = LocaleData.Translate("ticket.subject", "fr");messages = Escalated.Locale.get_locale_data("fr")
label = Escalated.Locale.t("ticket.subject", "fr")from escalated_locale import get_locale_data, get_django_locale_path
messages = get_locale_data("fr") # returns dict from locales/fr.json
# In Django settings.py, layer this dir into LOCALE_PATHS so
# escalated_locale/locale/<lang>/LC_MESSAGES/django.{po,mo} is picked up
# by gettext.
LOCALE_PATHS = [
BASE_DIR / "locale", # project overrides (highest priority)
str(get_django_locale_path()),
]import escalatedlocale "github.com/escalated-dev/escalated-locale/packages/go"
messages := escalatedlocale.GetLocaleData("fr")
label, _ := escalatedlocale.Translate("ticket.subject", "fr", nil)Semantic versioning applies to the canonical JSON keys and wrapper APIs:
- Major: key removals, key renames, or breaking wrapper API changes.
- Minor: new keys or new locales.
- Patch: string updates, fallback fixes, and packaging-only changes.