@urbicon-ui/i18n 8.25.0 → 8.26.0

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 pre-WP2 module 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()`.
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 (WP4) —
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 (WP4 code-splitting). Each returns the package's
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 (WP4): pass `options.loaders` to keep non-base locales
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 (WP4): pass `options.loaders` to keep non-base locales
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 (Codeberg #22).
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 (WP4): dynamic-import loaders alongside the eager base
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 (WP4 code-splitting). The
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 (WP4 code-splitting), keyed `${packageName}::${locale}`.
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 (WP4 code-splitting). The
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. But it is built on first
495
- // touch (not at module top-level) and reached through this hoisted function so
496
- // that a consumer chunk's top-level `createPackageI18n` side-effect — which
497
- // Vite 8 / Rolldown may order before this module's body — still finds a callable
498
- // binding and an initialised instance, instead of a value in the temporal dead
499
- // zone (the `Cannot read properties of undefined (reading 'registerPackage')`
500
- // class of bug the old lazy singleton guarded against).
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();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@urbicon-ui/i18n",
3
- "version": "8.25.0",
3
+ "version": "8.26.0",
4
4
  "description": "Runes-based localization for Svelte 5 apps and the Urbicon UI design system",
5
5
  "license": "MIT",
6
6
  "repository": {