From 583be85bd6bf8b87ecd420eaef5247cffa96550b Mon Sep 17 00:00:00 2001 From: Matt Gros <3311227+mpge@users.noreply.github.com> Date: Fri, 1 May 2026 22:08:54 -0400 Subject: [PATCH 1/5] feat(deps): add @escalated-dev/locale ^0.1.0 Adds the central locale package as a runtime dependency. The next commit wires it into the i18n loader as the BASE source, with local src/locales/ files retained as overrides until the central source is populated. --- package.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/package.json b/package.json index d916059..95793cf 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,9 @@ "@inertiajs/vue3": "^1.0.0 || ^2.0.0 || ^3.0.0", "vue": "^3.3.0" }, + "dependencies": { + "@escalated-dev/locale": "^0.1.0" + }, "scripts": { "test": "vitest run", "lint": "eslint src/ --max-warnings 0", From a2b8843508d0295f4243e996476bd630c145e7fb Mon Sep 17 00:00:00 2001 From: Matt Gros <3311227+mpge@users.noreply.github.com> Date: Fri, 1 May 2026 22:09:28 -0400 Subject: [PATCH 2/5] feat(i18n): layer central locale as base, local files as overrides MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wires useI18n() to consume @escalated-dev/locale as the canonical BASE translation source, with src/locales/*.json now treated as local plugin overrides that win over central. Lookup order: 1. host-app customMessages (current locale) 2. local override (src/locales/.json) 3. central source (@escalated-dev/locale) 4. host-app customMessages (en fallback) 5. local override (en fallback) 6. central source (en fallback) 7. raw key Public API is unchanged: components still call useI18n().t(key, repl) and $t works identically. No translation strings are modified by this commit — Codex will populate the central package separately, after which the local override files can be trimmed. Blocked on @escalated-dev/locale v0.1.0 publish; CI npm install will fail until then. --- src/composables/useI18n.js | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/src/composables/useI18n.js b/src/composables/useI18n.js index e23e8b5..adf98da 100644 --- a/src/composables/useI18n.js +++ b/src/composables/useI18n.js @@ -1,5 +1,17 @@ import { ref, computed } from 'vue'; -import locales from '../locales/index.js'; +import overrideLocales from '../locales/index.js'; + +// Central translations published from escalated-dev/escalated-locale. +// This is the BASE layer. Local src/locales/*.json files in this repo are +// loaded as OVERRIDES and win over the central source for any matching key. +// +// Until @escalated-dev/locale v0.1.0 ships its consolidated strings, this +// import resolves to a stub object with the same shape. Local overrides +// (src/locales/) preserve the current behavior so components keep rendering +// the strings they always rendered. After Codex finishes consolidating +// translations into the central package, the local override files can be +// trimmed (or deleted entirely) and the central source becomes authoritative. +import centralLocales from '@escalated-dev/locale'; const currentLocale = ref('en'); const customMessages = ref({}); @@ -55,21 +67,31 @@ export function useI18n() { function t(key, replacements) { const lang = currentLocale.value; - // Try custom messages for current locale + // 1. Host-app supplied messages for the current locale (highest priority). const custom = resolve(customMessages.value[lang], key); if (typeof custom === 'string') return applyReplacements(custom, replacements); - // Try built-in messages for current locale - const msg = resolve(locales[lang], key); - if (typeof msg === 'string') return applyReplacements(msg, replacements); + // 2. Local plugin overrides for the current locale (src/locales/*.json). + // These exist to let this repo win over the central source for any + // string the plugin needs to phrase differently. + const override = resolve(overrideLocales[lang], key); + if (typeof override === 'string') return applyReplacements(override, replacements); + + // 3. Central source for the current locale (@escalated-dev/locale). + // This is the canonical base layer. + const central = resolve(centralLocales?.[lang], key); + if (typeof central === 'string') return applyReplacements(central, replacements); - // Fallback to English + // 4. English fallbacks, in the same priority order. if (lang !== 'en') { const customEn = resolve(customMessages.value.en, key); if (typeof customEn === 'string') return applyReplacements(customEn, replacements); - const fallback = resolve(locales.en, key); - if (typeof fallback === 'string') return applyReplacements(fallback, replacements); + const overrideEn = resolve(overrideLocales.en, key); + if (typeof overrideEn === 'string') return applyReplacements(overrideEn, replacements); + + const centralEn = resolve(centralLocales?.en, key); + if (typeof centralEn === 'string') return applyReplacements(centralEn, replacements); } // Return raw key From 5d72a9093685cd2a3c28c4162591bf532e11439a Mon Sep 17 00:00:00 2001 From: Matt Gros <3311227+mpge@users.noreply.github.com> Date: Fri, 1 May 2026 22:10:07 -0400 Subject: [PATCH 3/5] docs(locales): document override layering for src/locales/ Clarifies that the JSON files in src/locales/ are now local overrides that win over the central @escalated-dev/locale source, and explains the resolution order used by useI18n().t(). --- src/locales/README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/locales/README.md diff --git a/src/locales/README.md b/src/locales/README.md new file mode 100644 index 0000000..c3deacb --- /dev/null +++ b/src/locales/README.md @@ -0,0 +1,13 @@ +# Local locale overrides + +The JSON files in this directory (`en.json`, `de.json`, …) are **local overrides** that win over the central source published by [`@escalated-dev/locale`](https://github.com/escalated-dev/escalated-locale). + +The composable in `src/composables/useI18n.js` resolves keys in this order: + +1. Host-app `customMessages` for the current locale +2. Local override (these JSON files) for the current locale +3. Central source (`@escalated-dev/locale`) for the current locale +4. Same three layers, but English fallback +5. Raw key + +After the central package is populated with the canonical strings, files here can be trimmed to contain only the keys this plugin needs to phrase differently from the central source. Keys not present locally fall through to the central source unchanged. From 6ec1bf6aa53b767378f7d33d355e7a97b06864ca Mon Sep 17 00:00:00 2001 From: Matt Gros <3311227+mpge@users.noreply.github.com> Date: Fri, 1 May 2026 22:10:14 -0400 Subject: [PATCH 4/5] docs(changelog): note central-locale wiring under Unreleased --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d838b7..3bf1753 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ All notable changes to `@escalated-dev/escalated` will be documented in this fil ## [Unreleased] +### Added +- feat: consume translations from `@escalated-dev/locale` as the canonical base source, with `src/locales/*.json` retained as local plugin overrides that win over central. Public `useI18n().t()` / `$t` API is unchanged. + ## [0.7.1] - 2026-04-28 ### Changed From 6ffd843f9347b5feb825154e37f72d8e68b9e29b Mon Sep 17 00:00:00 2001 From: Matt Gros <3311227+mpge@users.noreply.github.com> Date: Sat, 2 May 2026 15:35:54 -0400 Subject: [PATCH 5/5] build: pin @escalated-dev/locale to github tarball v0.1.2 The central package's root package.json shim landed at v0.1.2 in escalated-dev/escalated-locale, making github tarball deps resolve cleanly. This unblocks the central-locale PR; once @escalated-dev/locale publishes to the npm registry this can revert to a semver range. --- package-lock.json | 8 ++++++++ package.json | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 0d16241..9ef9926 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,9 @@ "name": "@escalated-dev/escalated", "version": "0.7.1", "license": "MIT", + "dependencies": { + "@escalated-dev/locale": "github:escalated-dev/escalated-locale#v0.1.2" + }, "devDependencies": { "@eslint/js": "^10.0.1", "@inertiajs/vue3": "^2.0.0", @@ -527,6 +530,11 @@ "node": ">=12" } }, + "node_modules/@escalated-dev/locale": { + "version": "0.1.2", + "resolved": "git+ssh://git@github.com/escalated-dev/escalated-locale.git#db1afd52b378529eeb9ede81862d85192f580a5c", + "license": "MIT" + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.9.1", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", diff --git a/package.json b/package.json index 95793cf..fb375b1 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "vue": "^3.3.0" }, "dependencies": { - "@escalated-dev/locale": "^0.1.0" + "@escalated-dev/locale": "github:escalated-dev/escalated-locale#v0.1.2" }, "scripts": { "test": "vitest run",