@urbicon-ui/i18n 8.25.0 → 8.26.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -344,7 +344,7 @@ import type {
|
|
|
344
344
|
} from '@urbicon-ui/i18n';
|
|
345
345
|
```
|
|
346
346
|
|
|
347
|
-
> **Breaking (major):** the
|
|
347
|
+
> **Breaking (major):** the module-level singleton (`i18n`, the free `t` / `plural`, `I18nService`) was **removed** — it leaked the locale across SSR requests. Mount `<I18nProvider>` / `provideI18n` and read through the hooks; replace `i18n.t(k)` → `useI18n().t(k)`, `bt(k)` → `const bt = useBlocksI18n()`.
|
|
348
348
|
|
|
349
349
|
## Supported Locales (Core)
|
|
350
350
|
|
|
@@ -108,7 +108,7 @@ export function provideI18n(locale, fallbackLocale = BASE_LOCALE) {
|
|
|
108
108
|
}
|
|
109
109
|
});
|
|
110
110
|
}
|
|
111
|
-
// Ensure the initial active + fallback locales' lazy bundles are loaded
|
|
111
|
+
// Ensure the initial active + fallback locales' lazy bundles are loaded —
|
|
112
112
|
// a no-op when everything is eager. Client-only (effects don't run during SSR),
|
|
113
113
|
// so a lazy non-base initial locale renders the fallback on the server and the
|
|
114
114
|
// first client paint, then re-resolves once the chunk lands. Subsequent switches
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { CreatePackageTypes, Locale, PackageI18n, PackageTranslations, TranslationParams, TranslationSchema, Translations, TypedTranslationFunction } from './types.js';
|
|
2
2
|
export interface CreatePackageI18nOptions {
|
|
3
3
|
/**
|
|
4
|
-
* Per-locale lazy loaders (
|
|
4
|
+
* Per-locale lazy loaders (code-splitting). Each returns the package's
|
|
5
5
|
* bundle for that locale as a dynamic-import chunk, e.g.
|
|
6
6
|
* `() => import('./translations/de').then((m) => m.default)`. Listed locales
|
|
7
7
|
* stay out of the initial bundle until activated by the provider / `setLocale`;
|
|
@@ -21,7 +21,7 @@ export interface CreatePackageI18nOptions {
|
|
|
21
21
|
* `TranslationSchema<T>` (T's structure with its string values widened), which
|
|
22
22
|
* enforces key parity at compile time while allowing locale-specific strings.
|
|
23
23
|
*
|
|
24
|
-
* Opt-in code-splitting
|
|
24
|
+
* Opt-in code-splitting: pass `options.loaders` to keep non-base locales
|
|
25
25
|
* out of the initial bundle as dynamic-import chunks, loaded only when activated.
|
|
26
26
|
*/
|
|
27
27
|
export declare function createPackageI18n<const T extends Translations>(packageName: string, translations: {
|
|
@@ -12,11 +12,11 @@ import { isLocaleSupported, SUPPORTED_LOCALES } from './types.js';
|
|
|
12
12
|
* `TranslationSchema<T>` (T's structure with its string values widened), which
|
|
13
13
|
* enforces key parity at compile time while allowing locale-specific strings.
|
|
14
14
|
*
|
|
15
|
-
* Opt-in code-splitting
|
|
15
|
+
* Opt-in code-splitting: pass `options.loaders` to keep non-base locales
|
|
16
16
|
* out of the initial bundle as dynamic-import chunks, loaded only when activated.
|
|
17
17
|
*/
|
|
18
18
|
export function createPackageI18n(packageName, translations, options) {
|
|
19
|
-
// Lazy, first-use registration — deliberately NOT at module-eval
|
|
19
|
+
// Lazy, first-use registration — deliberately NOT at module-eval.
|
|
20
20
|
// A consumer's top-level `export const x = createPackageI18n(...)` must not call
|
|
21
21
|
// getRegistry() during module initialisation: in a reordered *production* chunk
|
|
22
22
|
// (Rollup, `sideEffects: false`) that call can run before the registry module's
|
|
@@ -40,7 +40,7 @@ export function createPackageI18n(packageName, translations, options) {
|
|
|
40
40
|
registered = true;
|
|
41
41
|
const registry = getRegistry();
|
|
42
42
|
registry.registerPackage(packageName, translations);
|
|
43
|
-
// Opt-in lazy locales
|
|
43
|
+
// Opt-in lazy locales: dynamic-import loaders alongside the eager base
|
|
44
44
|
// bundle, loaded on demand by the provider / setLocale.
|
|
45
45
|
if (options?.loaders) {
|
|
46
46
|
for (const [locale, loader] of Object.entries(options.loaders)) {
|
|
@@ -47,7 +47,7 @@ export declare class I18nRegistry {
|
|
|
47
47
|
registerPackage(packageName: string, translations: PackageTranslations): void;
|
|
48
48
|
registerTranslationLoader(locale: Locale, loader: TranslationLoader): void;
|
|
49
49
|
/**
|
|
50
|
-
* Register a per-package lazy loader for one locale (
|
|
50
|
+
* Register a per-package lazy loader for one locale (code-splitting). The
|
|
51
51
|
* loader returns that package's bundle for `locale` (typically
|
|
52
52
|
* `() => import('./translations/de').then((m) => m.default)`), kept out of the
|
|
53
53
|
* initial chunk until the locale is activated.
|
|
@@ -27,7 +27,7 @@ export class I18nRegistry {
|
|
|
27
27
|
loadingLocales = new SvelteSet();
|
|
28
28
|
// Non-reactive per-instance cache; SvelteMap is unnecessary here.
|
|
29
29
|
translationLoaders = new Map();
|
|
30
|
-
// Per-package lazy loaders (
|
|
30
|
+
// Per-package lazy loaders (code-splitting), keyed `${packageName}::${locale}`.
|
|
31
31
|
// Registered at module-eval, read in setLocale/loadLocale (not in a $derived), so
|
|
32
32
|
// a plain Map suffices. The *loaded data* lands in the reactive packageTranslations,
|
|
33
33
|
// which is what re-resolves `$derived` reads when a chunk arrives.
|
|
@@ -101,7 +101,7 @@ export class I18nRegistry {
|
|
|
101
101
|
this.translationLoaders.set(locale, loader);
|
|
102
102
|
}
|
|
103
103
|
/**
|
|
104
|
-
* Register a per-package lazy loader for one locale (
|
|
104
|
+
* Register a per-package lazy loader for one locale (code-splitting). The
|
|
105
105
|
* loader returns that package's bundle for `locale` (typically
|
|
106
106
|
* `() => import('./translations/de').then((m) => m.default)`), kept out of the
|
|
107
107
|
* initial chunk until the locale is activated.
|
|
@@ -491,13 +491,15 @@ export class I18nRegistry {
|
|
|
491
491
|
}
|
|
492
492
|
// Lazy, hoisted accessor for the process-wide registry. Holding the registry at
|
|
493
493
|
// module scope is correct *because* it carries no per-request mutable locale —
|
|
494
|
-
// only static, request-identical translation data.
|
|
495
|
-
//
|
|
496
|
-
// that
|
|
497
|
-
//
|
|
498
|
-
//
|
|
499
|
-
//
|
|
500
|
-
//
|
|
494
|
+
// only static, request-identical translation data. The instance is built on the
|
|
495
|
+
// first call, not at module top-level. Hoisting keeps the *function* callable from
|
|
496
|
+
// a module that runs before this module's body, but it does not make an early call
|
|
497
|
+
// safe: natively, the first read of the `let _registry` binding below throws
|
|
498
|
+
// ("Cannot access '_registry' before initialization"); in a reordered bundled
|
|
499
|
+
// chunk the class binding is still unassigned, so it is `new (undefined)()`.
|
|
500
|
+
// Nothing may therefore call this during module initialisation — which is why
|
|
501
|
+
// `createPackageI18n` registers lazily, on first use (`ensureRegistered()`),
|
|
502
|
+
// rather than at module-eval.
|
|
501
503
|
let _registry;
|
|
502
504
|
export function getRegistry() {
|
|
503
505
|
_registry ??= new I18nRegistry();
|