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 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 d916059..fb375b1 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": "github:escalated-dev/escalated-locale#v0.1.2" + }, "scripts": { "test": "vitest run", "lint": "eslint src/ --max-warnings 0", 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 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.