@i18n-micro/types 1.2.18 → 2.0.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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2016
3
+ Copyright (c) 2024 s00d
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/dist/index.d.cts CHANGED
@@ -77,14 +77,17 @@ export interface Locale {
77
77
  /** Human-readable name shown in language switchers (e.g. `'English'`, `'Deutsch'`). */
78
78
  displayName?: string;
79
79
  /**
80
- * Per-locale base URL for SEO meta tags.
81
- * Useful for multi-domain setups where each locale lives on its own domain
82
- * (e.g. `'https://en.example.com'`).
80
+ * Origin of this locale's own domain (e.g. `'https://de.example.com'`), plus `app.baseURL` when
81
+ * the app has one. One build then serves every domain: a request for that host gets this locale,
82
+ * the other locales of the host and the locales with no `baseUrl`. Links into a locale of another
83
+ * domain (`$localePath`, `$switchLocalePath`, the switcher, `hreflang`) become absolute URLs on
84
+ * its origin, and switching to it navigates there. Any other host gets the build config.
83
85
  */
84
86
  baseUrl?: string;
85
87
  /**
86
- * When `true`, this locale is treated as the "base default" for the domain
87
- * specified in `baseUrl`. Affects canonical URL generation in multi-domain setups.
88
+ * With `baseUrl`: this locale is the default of its host, unprefixed there, and links to it from
89
+ * other domains drop `/<code>`, because the domain already names the locale
90
+ * (`https://de.example.com/about`, not `…/de/about`). `prefix` keeps the prefix in both.
88
91
  */
89
92
  baseDefault?: boolean;
90
93
  /**
@@ -96,7 +99,7 @@ export interface Locale {
96
99
  [key: string]: unknown;
97
100
  }
98
101
  /**
99
- * Per-page i18n configuration declared via `defineI18nRoute()` in page components.
102
+ * Per-page i18n configuration declared via `$defineI18nRoute()` in page components.
100
103
  */
101
104
  export interface DefineI18nRouteConfig {
102
105
  /**
@@ -184,6 +187,12 @@ export interface ModuleOptions {
184
187
  * @default true
185
188
  */
186
189
  metaTrustForwardedProto?: boolean;
190
+ /**
191
+ * Choose the domain of a request (`locales[].baseUrl`) from `X-Forwarded-Host` instead of
192
+ * `Host`. Enable it only behind a proxy that overwrites that header.
193
+ * @default false
194
+ */
195
+ trustForwardedHost?: boolean;
187
196
  /**
188
197
  * Also emit a bare-language `hreflang` derived from each locale's `iso`
189
198
  * (e.g. `es-ES` → also `es`). The first regional locale in `locales` claims
@@ -192,14 +201,16 @@ export interface ModuleOptions {
192
201
  */
193
202
  hreflangBaseLanguage?: boolean;
194
203
  /**
195
- * Register the `defineI18nRoute()` macro plugin, enabling per-page `defineI18nRoute()` calls.
204
+ * Provide `$defineI18nRoute()` at runtime. The build reads `$defineI18nRoute()` calls in pages
205
+ * either way.
196
206
  * @default true
197
207
  */
198
208
  define?: boolean;
199
209
  /**
200
- * Enable automatic locale-based redirects.
201
- * When `true`, visitors are redirected to their preferred locale (detected from cookie,
202
- * `Accept-Language` header, or the default) on the first visit.
210
+ * Enable automatic locale redirects (server plugin and client route middleware).
211
+ * When `true`, the prefix strategies redirect an unprefixed URL to its localized form. The locale
212
+ * comes from the app state, the cookie or `Accept-Language` where `autoDetectPath` allows, otherwise
213
+ * the default. Under `no_prefix` detection never changes the URL (see `noPrefixRedirect` for custom paths).
203
214
  * @default true
204
215
  */
205
216
  redirects?: boolean;
@@ -210,8 +221,7 @@ export interface ModuleOptions {
210
221
  */
211
222
  plugin?: boolean;
212
223
  /**
213
- * Register the i18n hooks plugin that provides `i18n:register` and `i18n:beforeLocaleSwitch`
214
- * / `i18n:afterLocaleSwitch` app-level hooks.
224
+ * Call the `i18n:register` app hook on load and on navigation so plugins can add translations.
215
225
  * @default true
216
226
  */
217
227
  hooks?: boolean;
@@ -229,7 +239,7 @@ export interface ModuleOptions {
229
239
  serverTranslationPreload?: boolean;
230
240
  /**
231
241
  * The locale to use when no locale can be determined from URL or user preferences.
232
- * Also used as the fallback locale for missing translations when `fallbackLocale` is not set.
242
+ * It is not a translation fallback: set `fallbackLocale` for that.
233
243
  * @default 'en'
234
244
  */
235
245
  defaultLocale?: string;
@@ -292,9 +302,11 @@ export interface ModuleOptions {
292
302
  * - `'no_prefix'` — only paths without a locale prefix
293
303
  * - `'*'` — every path, including rewriting an explicit locale prefix
294
304
  * (e.g. `/fr/about` → `/de/about` when the cookie prefers `de`)
295
- * - any other string — exact path match (e.g. `'/welcome'`)
305
+ * - any other string — that path, trailing slashes ignored (e.g. `'/welcome'`)
296
306
  *
297
- * Prefixed strategy cleanup (e.g. `/en` → `/` under `prefix_except_default`) is not gated.
307
+ * Under `prefix`, an unprefixed URL outside the allowed paths still redirects to the default
308
+ * locale's form (`/about` → `/en/about`). The default locale with a prefix under
309
+ * `prefix_except_default` (`/en`) is a 404, not a redirect.
298
310
  * @default '/'
299
311
  */
300
312
  autoDetectPath?: string;
@@ -327,7 +339,7 @@ export interface ModuleOptions {
327
339
  * For the Nuxt module the function is serialized with `.toString()` into
328
340
  * `.nuxt/i18n.plural.mjs` — it must be self-contained (no imports / outer scope).
329
341
  * A file path string is **not** supported.
330
- * @default built-in pluralization (form index by count)
342
+ * @default built-in pluralization (a non-negative integer count picks the form at that index, clamped to the last; negative, fractional and `NaN` counts take the last; `=0 ` opens a template of `Intl.PluralRules` categories)
331
343
  */
332
344
  plural?: PluralFunc;
333
345
  /**
@@ -339,15 +351,16 @@ export interface ModuleOptions {
339
351
  disablePageLocales?: boolean;
340
352
  /**
341
353
  * Global fallback locale code.
342
- * When a translation key is missing in the active locale, the module looks it up
343
- * in this locale before returning the key itself.
354
+ * Keys missing in a locale are taken from this locale (merged into each payload when it is
355
+ * built or loaded); without it a missing key renders as the key itself.
344
356
  * @default undefined (no fallback; returns the raw key)
345
357
  */
346
358
  fallbackLocale?: string;
347
359
  /**
348
360
  * Cookie name for persisting the user's locale preference across sessions.
349
361
  * Set to `null` to disable cookie-based persistence.
350
- * Automatically set to `'user-locale'` for the `no_prefix` strategy if not provided.
362
+ * Automatically set to `'user-locale'` for the `no_prefix` strategy if not provided; an explicit
363
+ * `null` in the `i18n` config block keeps it off there too.
351
364
  * @default null
352
365
  */
353
366
  localeCookie?: string | null;
@@ -373,13 +386,16 @@ export interface ModuleOptions {
373
386
  globalLocaleRoutes?: GlobalLocaleRoutes;
374
387
  /**
375
388
  * Custom regular expression (or its string source) for matching locale codes in URL segments.
376
- * All locale codes defined in `locales` must match this pattern, or a warning is emitted.
389
+ * Every locale code in `locales` must match the whole pattern, without its flags, or the build fails.
377
390
  * @default undefined (uses built-in pattern based on locale codes)
378
391
  */
379
392
  customRegexMatcher?: string | RegExp;
380
393
  /**
381
- * For `no_prefix` strategy: enable redirect from a locale-prefixed URL
382
- * (e.g. `/en/about`) to the unprefixed version (`/about`).
394
+ * For `no_prefix` strategy: when the default locale has a custom path for a page
395
+ * (`localeRoutes` / `globalLocaleRoutes`), redirect the page's original path to the custom
396
+ * path of the visitor's locale, the default locale's otherwise (e.g. `/about` → `/about-us`).
397
+ * A locale with no custom path of its own stays on the original path. Pages with dynamic
398
+ * segments are not redirected.
383
399
  * @default false
384
400
  */
385
401
  noPrefixRedirect?: boolean;
@@ -397,9 +413,11 @@ export interface ModuleOptions {
397
413
  */
398
414
  trailingSlash?: 'append' | 'remove';
399
415
  /**
400
- * URL patterns (strings or RegExp) to exclude from i18n processing entirely.
401
- * Matching routes won't get locale prefixes, redirects, or translation loading.
402
- * Internal Nuxt paths (`/__nuxt_error`, etc.) are always excluded automatically.
416
+ * Paths to leave out of i18n routing: a string is a path prefix (`/admin` covers `/admin/x`)
417
+ * or a `*` / `?` glob; a RegExp is tested against the path. Matching pages get no locale
418
+ * variants, no locale redirect or 404, and are dropped from prerender; they still render with
419
+ * translations. `/api`, `/_nuxt`, `/__*`, the translations route and common static files
420
+ * (sitemap, robots.txt, favicon, assets by extension) are always excluded.
403
421
  * @default undefined
404
422
  */
405
423
  excludePatterns?: (string | RegExp)[];
@@ -410,19 +428,19 @@ export interface ModuleOptions {
410
428
  */
411
429
  localizedRouteNamePrefix?: string;
412
430
  /**
413
- * Per-route locale restrictions, extracted from `defineI18nRoute()` calls.
431
+ * Per-route locale restrictions, extracted at build time from `$defineI18nRoute()` calls.
414
432
  * Maps a route path (e.g. `'/about'`) to an array of allowed locale codes.
415
433
  * Routes not listed have no restrictions (all locales allowed).
416
434
  */
417
435
  routeLocales?: Record<string, string[]>;
418
436
  /**
419
- * Per-route meta tag disabling, extracted from `defineI18nRoute()` calls.
420
- * Maps a route path to `true` (disable all meta) or an array of locale codes
421
- * for which meta should be disabled.
437
+ * Per-route meta tag disabling. Maps a route name or path to `true` (disable all meta) or an array
438
+ * of locale codes for which meta should be disabled. Entries found in `$defineI18nRoute()`
439
+ * calls at build time are added to it.
422
440
  */
423
441
  routeDisableMeta?: Record<string, boolean | string[]>;
424
442
  /**
425
- * Show console warnings when a translation key is missing.
443
+ * Show console warnings when a translation key is missing (development build, in the browser).
426
444
  * @default true
427
445
  */
428
446
  missingWarn?: boolean;
@@ -434,17 +452,29 @@ export interface ModuleOptions {
434
452
  */
435
453
  hmr?: boolean;
436
454
  /**
437
- * Maximum number of entries in the in-memory translation cache.
438
- * `0` means no limit.
455
+ * Maximum number of entries in the in-memory translation caches. The fetch and server caches
456
+ * drop the least recently used entry; the browser's dictionary map drops the oldest one (FIFO).
457
+ * `0` means no limit in the browser; server caches keep at most 1000 entries then, so request
458
+ * URLs naming made-up pages cannot grow server memory.
439
459
  * @default 0
440
460
  */
441
461
  cacheMaxSize?: number;
442
462
  /**
443
- * Time-to-live (in seconds) for cached translation entries.
444
- * `0` means entries never expire.
463
+ * Time-to-live (in seconds) of the fetch and server translation caches, counted from when an
464
+ * entry was stored (reads do not extend it), so data from `apiBaseServerHost` is refreshed at
465
+ * least that often. The browser's active dictionaries are not expired by it. `0` means entries
466
+ * never expire.
445
467
  * @default 0
446
468
  */
447
469
  cacheTtl?: number;
470
+ /**
471
+ * Name of a Nitro storage mount (`nitro.storage`, e.g. `'redis'`) that several server instances
472
+ * share as a dictionary cache, read after the in-process cache and before the source. Pays off
473
+ * mostly with `apiBaseServerHost`: one fetch from the backend instead of one per instance.
474
+ * Entries are keyed by build and expire after `cacheTtl`, or after one day when it is `0`.
475
+ * Off in dev and prerender. A failing storage falls back to the source. Unset: no shared cache.
476
+ */
477
+ cacheStorage?: string;
448
478
  /**
449
479
  * Named number formats per locale (Vue I18n-compatible).
450
480
  * Enables `$tn(1000, 'currency')` style calls.
@@ -456,6 +486,12 @@ export interface ModuleOptions {
456
486
  * }
457
487
  */
458
488
  numberFormats?: Record<string, Record<string, Intl.NumberFormatOptions>>;
489
+ /**
490
+ * IANA time zone for `$td` and `<i18n-t date>` (`Europe/Berlin`). Set it to render one date on
491
+ * the server and in every browser; a named format's own `timeZone` wins.
492
+ * @default the runtime's zone
493
+ */
494
+ timeZone?: string;
459
495
  /**
460
496
  * Named datetime formats per locale (Vue I18n-compatible `datetimeFormats`).
461
497
  * Enables `$td(date, 'short')` style calls.
@@ -471,7 +507,8 @@ export interface ModuleOptions {
471
507
  * HTTP `Cache-Control` max-age (seconds) for `/{apiBaseUrl}/:page/:locale/data.json`.
472
508
  *
473
509
  * Applies in full only while `dateBuild` busts the URL (`?v=...`), which is the default:
474
- * the response is then `public, max-age=…, immutable` and safe for browsers and CDNs.
510
+ * the response is then `private, max-age=…, immutable`: a browser pins it, and a CDN that ignores the query cannot
511
+ * (`public` with `httpCacheControl: false`).
475
512
  *
476
513
  * - `dateBuild: 0`/`''` — the URL is stable, so this duration is *not* honoured; the
477
514
  * response becomes `public, max-age=0, must-revalidate` instead. A long `max-age` on an
@@ -486,12 +523,22 @@ export interface ModuleOptions {
486
523
  * @default 31536000
487
524
  */
488
525
  httpCacheDuration?: number;
526
+ /**
527
+ * `Cache-Control` of a payload file answered by a host or by a path-wide rule, which cannot see `?v=`.
528
+ * On a Node server a response to the current `?v=` is pinned for `httpCacheDuration` and any other revalidates;
529
+ * with `source`, `cacheTtl` or `apiBaseServerHost`, where a dictionary changes with no build, every response takes this header.
530
+ * `false` keeps every file on `max-age=0, must-revalidate`.
531
+ * @default 'public, max-age=60'
532
+ */
533
+ httpCacheControl?: string | false;
489
534
  /**
490
535
  * Value used for cache-busting translation requests (`?v=...`).
491
536
  *
492
- * When not provided, the module falls back to `Date.now()` (non-deterministic).
493
- * For reproducible/rolling deployments, set this to a stable value
494
- * (e.g. a git SHA or build number).
537
+ * When not provided, the module uses a hash of the translation files, so the value changes
538
+ * only when translations do (`Date.now()` when no hash can be computed). `0` or `''` turns
539
+ * cache-busting off. Set a stable value (a git SHA, a build number) to pin it yourself.
540
+ * `NUXT_I18N_DATE_BUILD` or `runtimeConfig.public.i18nRuntime.dateBuild` replaces it at runtime, after a dictionary
541
+ * was replaced in place: pinned URLs are left behind, with no rebuild.
495
542
  *
496
543
  * @example
497
544
  * i18n: { dateBuild: 'e3b0c442' }
@@ -549,6 +596,13 @@ export interface TranslationPayloadOptions {
549
596
  * So static files match client fetch URLs (`/{apiBaseUrl}/{page}/{locale}/data.json`).
550
597
  */
551
598
  publicDir?: string;
599
+ /**
600
+ * Write one root file per locale, and in each page file only what differs from it. The client and SSR merge them;
601
+ * a whole chunk still works wherever a file is read. `false` keeps a whole dictionary in every page file.
602
+ * `premerged` only, and off with `disablePageLocales`.
603
+ * @default true
604
+ */
605
+ rootDelta?: boolean;
552
606
  /**
553
607
  * Warn during build when generated payload file count exceeds this threshold.
554
608
  * @default 500
@@ -569,8 +623,8 @@ export interface ModuleOptionsExtend extends ModuleOptions {
569
623
  dateBuild: string | number;
570
624
  /** Whether the Nuxt app uses hash-based routing (`router.options.hashMode`). */
571
625
  hashMode: boolean;
572
- /** Whether the current build is a static site generation (SSG / `nuxi generate`). */
573
- isSSG: boolean;
626
+ /** @deprecated No longer set at runtime. Kept so code typed against the released module still compiles. */
627
+ isSSG?: boolean;
574
628
  /** Resolved translations API base URL (after env variable / config merging). */
575
629
  apiBaseUrl: string;
576
630
  /** Resolved flag: whether per-page translations are disabled. */
@@ -579,14 +633,30 @@ export interface ModuleOptionsExtend extends ModuleOptions {
579
633
  redirects?: boolean;
580
634
  /** Resolved translation payload loading strategy. */
581
635
  translationPayloadMode?: 'premerged' | 'source';
636
+ /** Premerged files are in `public/`, so they can sit on `app.cdnURL`. */
637
+ publicPayloads?: boolean;
638
+ /** A page's file needs its root: the browser loads the two together. */
639
+ rootDelta?: boolean;
640
+ /** Nuxt's `features.noScripts` is on: no page runs a script, so none preloads a dictionary. */
641
+ noScripts?: boolean;
642
+ /** Host this config was resolved for (`locales[].baseUrl`); unset on any other host. */
643
+ domain?: string;
644
+ /** With `domain`: every locale, those of other domains included. `locales` holds what the host serves. */
645
+ linkLocales?: Locale[];
646
+ /** With `domain`: the build's `defaultLocale`, the same on every host (`x-default`). `defaultLocale` is the host's. */
647
+ linkDefaultLocale?: string;
582
648
  }
649
+ /** The keys `buildFullConfig` always sets: read at runtime with no default of their own. */
650
+ type ResolvedI18nConfigKey = 'apiBaseUrl' | 'autoDetectPath' | 'cacheMaxSize' | 'cacheTtl' | 'canonicalQueryWhitelist' | 'defaultLocale' | 'hreflangBaseLanguage' | 'locales' | 'missingWarn' | 'strategy';
651
+ /** The config the build writes, as `getI18nConfig()` and `$getI18nConfig()` give it. */
652
+ export type ResolvedI18nConfig = Omit<ModuleOptionsExtend, ResolvedI18nConfigKey> & Required<Pick<ModuleOptionsExtend, ResolvedI18nConfigKey>>;
583
653
  /**
584
654
  * Private (server-only) module options passed to Nitro plugins and server middleware.
585
655
  * Extends `ModuleOptions` with resolved root paths and directory info.
586
656
  */
587
657
  export interface ModulePrivateOptionsExtend extends ModuleOptions {
588
- /** Absolute path to the Nuxt project root directory. */
589
- rootDir: string;
658
+ /** Nuxt's `buildId`: `cacheStorage` keys of two builds never meet. */
659
+ cacheStorageBuild?: string;
590
660
  /**
591
661
  * Absolute Nuxt layer roots in merge order (extends first, project last).
592
662
  * Used by live/HMR translation reads so layers match build-time merge.
@@ -596,20 +666,8 @@ export interface ModulePrivateOptionsExtend extends ModuleOptions {
596
666
  debug: boolean;
597
667
  /** Resolved path to the translations directory. */
598
668
  translationDir: string;
599
- /** Resolved fallback locale code. */
600
- fallbackLocale: string;
601
- /** Resolved translations API base URL. */
602
- apiBaseUrl: string;
603
- /** Resolved client-side host override. */
604
- apiBaseClientHost?: string;
605
669
  /** Resolved server-side host override. */
606
670
  apiBaseServerHost?: string;
607
- /** Resolved custom regex matcher (RegExp is serialized to its `.source` string). */
608
- customRegexMatcher?: string | RegExp;
609
- /** Resolved route-to-route translation sharing map. */
610
- routesLocaleLinks?: {
611
- [key: string]: string;
612
- };
613
671
  /** Preload index translations in Nitro global middleware. */
614
672
  serverTranslationPreload?: boolean;
615
673
  /**
@@ -673,3 +731,16 @@ export interface I18nHeadInput {
673
731
  }
674
732
  declare const init: () => void;
675
733
  export { init };
734
+ /** `meta.i18n` that route-strategy puts on every localized record, and path-strategy reads at runtime. */
735
+ export interface RouteI18nMeta {
736
+ /** Base route name of the page (its translation key and the name `localeRoute({ name })` takes). */
737
+ name: string;
738
+ /** Locales this record serves. */
739
+ locales: string[];
740
+ /** `disableMeta` of the page: SEO tags off in every locale (`true`) or in the listed ones. */
741
+ noMeta?: boolean | string[];
742
+ /** On the default locale's record: the path of a page with a custom default path, where it is no record of its own. A link can still name the page by it. */
743
+ original?: string;
744
+ /** `noPrefixRedirect`, on the page's original path: the custom path of each locale. The locale is the visitor's, so the runtime redirects. */
745
+ paths?: Record<string, string>;
746
+ }
package/dist/index.d.ts CHANGED
@@ -77,14 +77,17 @@ export interface Locale {
77
77
  /** Human-readable name shown in language switchers (e.g. `'English'`, `'Deutsch'`). */
78
78
  displayName?: string;
79
79
  /**
80
- * Per-locale base URL for SEO meta tags.
81
- * Useful for multi-domain setups where each locale lives on its own domain
82
- * (e.g. `'https://en.example.com'`).
80
+ * Origin of this locale's own domain (e.g. `'https://de.example.com'`), plus `app.baseURL` when
81
+ * the app has one. One build then serves every domain: a request for that host gets this locale,
82
+ * the other locales of the host and the locales with no `baseUrl`. Links into a locale of another
83
+ * domain (`$localePath`, `$switchLocalePath`, the switcher, `hreflang`) become absolute URLs on
84
+ * its origin, and switching to it navigates there. Any other host gets the build config.
83
85
  */
84
86
  baseUrl?: string;
85
87
  /**
86
- * When `true`, this locale is treated as the "base default" for the domain
87
- * specified in `baseUrl`. Affects canonical URL generation in multi-domain setups.
88
+ * With `baseUrl`: this locale is the default of its host, unprefixed there, and links to it from
89
+ * other domains drop `/<code>`, because the domain already names the locale
90
+ * (`https://de.example.com/about`, not `…/de/about`). `prefix` keeps the prefix in both.
88
91
  */
89
92
  baseDefault?: boolean;
90
93
  /**
@@ -96,7 +99,7 @@ export interface Locale {
96
99
  [key: string]: unknown;
97
100
  }
98
101
  /**
99
- * Per-page i18n configuration declared via `defineI18nRoute()` in page components.
102
+ * Per-page i18n configuration declared via `$defineI18nRoute()` in page components.
100
103
  */
101
104
  export interface DefineI18nRouteConfig {
102
105
  /**
@@ -184,6 +187,12 @@ export interface ModuleOptions {
184
187
  * @default true
185
188
  */
186
189
  metaTrustForwardedProto?: boolean;
190
+ /**
191
+ * Choose the domain of a request (`locales[].baseUrl`) from `X-Forwarded-Host` instead of
192
+ * `Host`. Enable it only behind a proxy that overwrites that header.
193
+ * @default false
194
+ */
195
+ trustForwardedHost?: boolean;
187
196
  /**
188
197
  * Also emit a bare-language `hreflang` derived from each locale's `iso`
189
198
  * (e.g. `es-ES` → also `es`). The first regional locale in `locales` claims
@@ -192,14 +201,16 @@ export interface ModuleOptions {
192
201
  */
193
202
  hreflangBaseLanguage?: boolean;
194
203
  /**
195
- * Register the `defineI18nRoute()` macro plugin, enabling per-page `defineI18nRoute()` calls.
204
+ * Provide `$defineI18nRoute()` at runtime. The build reads `$defineI18nRoute()` calls in pages
205
+ * either way.
196
206
  * @default true
197
207
  */
198
208
  define?: boolean;
199
209
  /**
200
- * Enable automatic locale-based redirects.
201
- * When `true`, visitors are redirected to their preferred locale (detected from cookie,
202
- * `Accept-Language` header, or the default) on the first visit.
210
+ * Enable automatic locale redirects (server plugin and client route middleware).
211
+ * When `true`, the prefix strategies redirect an unprefixed URL to its localized form. The locale
212
+ * comes from the app state, the cookie or `Accept-Language` where `autoDetectPath` allows, otherwise
213
+ * the default. Under `no_prefix` detection never changes the URL (see `noPrefixRedirect` for custom paths).
203
214
  * @default true
204
215
  */
205
216
  redirects?: boolean;
@@ -210,8 +221,7 @@ export interface ModuleOptions {
210
221
  */
211
222
  plugin?: boolean;
212
223
  /**
213
- * Register the i18n hooks plugin that provides `i18n:register` and `i18n:beforeLocaleSwitch`
214
- * / `i18n:afterLocaleSwitch` app-level hooks.
224
+ * Call the `i18n:register` app hook on load and on navigation so plugins can add translations.
215
225
  * @default true
216
226
  */
217
227
  hooks?: boolean;
@@ -229,7 +239,7 @@ export interface ModuleOptions {
229
239
  serverTranslationPreload?: boolean;
230
240
  /**
231
241
  * The locale to use when no locale can be determined from URL or user preferences.
232
- * Also used as the fallback locale for missing translations when `fallbackLocale` is not set.
242
+ * It is not a translation fallback: set `fallbackLocale` for that.
233
243
  * @default 'en'
234
244
  */
235
245
  defaultLocale?: string;
@@ -292,9 +302,11 @@ export interface ModuleOptions {
292
302
  * - `'no_prefix'` — only paths without a locale prefix
293
303
  * - `'*'` — every path, including rewriting an explicit locale prefix
294
304
  * (e.g. `/fr/about` → `/de/about` when the cookie prefers `de`)
295
- * - any other string — exact path match (e.g. `'/welcome'`)
305
+ * - any other string — that path, trailing slashes ignored (e.g. `'/welcome'`)
296
306
  *
297
- * Prefixed strategy cleanup (e.g. `/en` → `/` under `prefix_except_default`) is not gated.
307
+ * Under `prefix`, an unprefixed URL outside the allowed paths still redirects to the default
308
+ * locale's form (`/about` → `/en/about`). The default locale with a prefix under
309
+ * `prefix_except_default` (`/en`) is a 404, not a redirect.
298
310
  * @default '/'
299
311
  */
300
312
  autoDetectPath?: string;
@@ -327,7 +339,7 @@ export interface ModuleOptions {
327
339
  * For the Nuxt module the function is serialized with `.toString()` into
328
340
  * `.nuxt/i18n.plural.mjs` — it must be self-contained (no imports / outer scope).
329
341
  * A file path string is **not** supported.
330
- * @default built-in pluralization (form index by count)
342
+ * @default built-in pluralization (a non-negative integer count picks the form at that index, clamped to the last; negative, fractional and `NaN` counts take the last; `=0 ` opens a template of `Intl.PluralRules` categories)
331
343
  */
332
344
  plural?: PluralFunc;
333
345
  /**
@@ -339,15 +351,16 @@ export interface ModuleOptions {
339
351
  disablePageLocales?: boolean;
340
352
  /**
341
353
  * Global fallback locale code.
342
- * When a translation key is missing in the active locale, the module looks it up
343
- * in this locale before returning the key itself.
354
+ * Keys missing in a locale are taken from this locale (merged into each payload when it is
355
+ * built or loaded); without it a missing key renders as the key itself.
344
356
  * @default undefined (no fallback; returns the raw key)
345
357
  */
346
358
  fallbackLocale?: string;
347
359
  /**
348
360
  * Cookie name for persisting the user's locale preference across sessions.
349
361
  * Set to `null` to disable cookie-based persistence.
350
- * Automatically set to `'user-locale'` for the `no_prefix` strategy if not provided.
362
+ * Automatically set to `'user-locale'` for the `no_prefix` strategy if not provided; an explicit
363
+ * `null` in the `i18n` config block keeps it off there too.
351
364
  * @default null
352
365
  */
353
366
  localeCookie?: string | null;
@@ -373,13 +386,16 @@ export interface ModuleOptions {
373
386
  globalLocaleRoutes?: GlobalLocaleRoutes;
374
387
  /**
375
388
  * Custom regular expression (or its string source) for matching locale codes in URL segments.
376
- * All locale codes defined in `locales` must match this pattern, or a warning is emitted.
389
+ * Every locale code in `locales` must match the whole pattern, without its flags, or the build fails.
377
390
  * @default undefined (uses built-in pattern based on locale codes)
378
391
  */
379
392
  customRegexMatcher?: string | RegExp;
380
393
  /**
381
- * For `no_prefix` strategy: enable redirect from a locale-prefixed URL
382
- * (e.g. `/en/about`) to the unprefixed version (`/about`).
394
+ * For `no_prefix` strategy: when the default locale has a custom path for a page
395
+ * (`localeRoutes` / `globalLocaleRoutes`), redirect the page's original path to the custom
396
+ * path of the visitor's locale, the default locale's otherwise (e.g. `/about` → `/about-us`).
397
+ * A locale with no custom path of its own stays on the original path. Pages with dynamic
398
+ * segments are not redirected.
383
399
  * @default false
384
400
  */
385
401
  noPrefixRedirect?: boolean;
@@ -397,9 +413,11 @@ export interface ModuleOptions {
397
413
  */
398
414
  trailingSlash?: 'append' | 'remove';
399
415
  /**
400
- * URL patterns (strings or RegExp) to exclude from i18n processing entirely.
401
- * Matching routes won't get locale prefixes, redirects, or translation loading.
402
- * Internal Nuxt paths (`/__nuxt_error`, etc.) are always excluded automatically.
416
+ * Paths to leave out of i18n routing: a string is a path prefix (`/admin` covers `/admin/x`)
417
+ * or a `*` / `?` glob; a RegExp is tested against the path. Matching pages get no locale
418
+ * variants, no locale redirect or 404, and are dropped from prerender; they still render with
419
+ * translations. `/api`, `/_nuxt`, `/__*`, the translations route and common static files
420
+ * (sitemap, robots.txt, favicon, assets by extension) are always excluded.
403
421
  * @default undefined
404
422
  */
405
423
  excludePatterns?: (string | RegExp)[];
@@ -410,19 +428,19 @@ export interface ModuleOptions {
410
428
  */
411
429
  localizedRouteNamePrefix?: string;
412
430
  /**
413
- * Per-route locale restrictions, extracted from `defineI18nRoute()` calls.
431
+ * Per-route locale restrictions, extracted at build time from `$defineI18nRoute()` calls.
414
432
  * Maps a route path (e.g. `'/about'`) to an array of allowed locale codes.
415
433
  * Routes not listed have no restrictions (all locales allowed).
416
434
  */
417
435
  routeLocales?: Record<string, string[]>;
418
436
  /**
419
- * Per-route meta tag disabling, extracted from `defineI18nRoute()` calls.
420
- * Maps a route path to `true` (disable all meta) or an array of locale codes
421
- * for which meta should be disabled.
437
+ * Per-route meta tag disabling. Maps a route name or path to `true` (disable all meta) or an array
438
+ * of locale codes for which meta should be disabled. Entries found in `$defineI18nRoute()`
439
+ * calls at build time are added to it.
422
440
  */
423
441
  routeDisableMeta?: Record<string, boolean | string[]>;
424
442
  /**
425
- * Show console warnings when a translation key is missing.
443
+ * Show console warnings when a translation key is missing (development build, in the browser).
426
444
  * @default true
427
445
  */
428
446
  missingWarn?: boolean;
@@ -434,17 +452,29 @@ export interface ModuleOptions {
434
452
  */
435
453
  hmr?: boolean;
436
454
  /**
437
- * Maximum number of entries in the in-memory translation cache.
438
- * `0` means no limit.
455
+ * Maximum number of entries in the in-memory translation caches. The fetch and server caches
456
+ * drop the least recently used entry; the browser's dictionary map drops the oldest one (FIFO).
457
+ * `0` means no limit in the browser; server caches keep at most 1000 entries then, so request
458
+ * URLs naming made-up pages cannot grow server memory.
439
459
  * @default 0
440
460
  */
441
461
  cacheMaxSize?: number;
442
462
  /**
443
- * Time-to-live (in seconds) for cached translation entries.
444
- * `0` means entries never expire.
463
+ * Time-to-live (in seconds) of the fetch and server translation caches, counted from when an
464
+ * entry was stored (reads do not extend it), so data from `apiBaseServerHost` is refreshed at
465
+ * least that often. The browser's active dictionaries are not expired by it. `0` means entries
466
+ * never expire.
445
467
  * @default 0
446
468
  */
447
469
  cacheTtl?: number;
470
+ /**
471
+ * Name of a Nitro storage mount (`nitro.storage`, e.g. `'redis'`) that several server instances
472
+ * share as a dictionary cache, read after the in-process cache and before the source. Pays off
473
+ * mostly with `apiBaseServerHost`: one fetch from the backend instead of one per instance.
474
+ * Entries are keyed by build and expire after `cacheTtl`, or after one day when it is `0`.
475
+ * Off in dev and prerender. A failing storage falls back to the source. Unset: no shared cache.
476
+ */
477
+ cacheStorage?: string;
448
478
  /**
449
479
  * Named number formats per locale (Vue I18n-compatible).
450
480
  * Enables `$tn(1000, 'currency')` style calls.
@@ -456,6 +486,12 @@ export interface ModuleOptions {
456
486
  * }
457
487
  */
458
488
  numberFormats?: Record<string, Record<string, Intl.NumberFormatOptions>>;
489
+ /**
490
+ * IANA time zone for `$td` and `<i18n-t date>` (`Europe/Berlin`). Set it to render one date on
491
+ * the server and in every browser; a named format's own `timeZone` wins.
492
+ * @default the runtime's zone
493
+ */
494
+ timeZone?: string;
459
495
  /**
460
496
  * Named datetime formats per locale (Vue I18n-compatible `datetimeFormats`).
461
497
  * Enables `$td(date, 'short')` style calls.
@@ -471,7 +507,8 @@ export interface ModuleOptions {
471
507
  * HTTP `Cache-Control` max-age (seconds) for `/{apiBaseUrl}/:page/:locale/data.json`.
472
508
  *
473
509
  * Applies in full only while `dateBuild` busts the URL (`?v=...`), which is the default:
474
- * the response is then `public, max-age=…, immutable` and safe for browsers and CDNs.
510
+ * the response is then `private, max-age=…, immutable`: a browser pins it, and a CDN that ignores the query cannot
511
+ * (`public` with `httpCacheControl: false`).
475
512
  *
476
513
  * - `dateBuild: 0`/`''` — the URL is stable, so this duration is *not* honoured; the
477
514
  * response becomes `public, max-age=0, must-revalidate` instead. A long `max-age` on an
@@ -486,12 +523,22 @@ export interface ModuleOptions {
486
523
  * @default 31536000
487
524
  */
488
525
  httpCacheDuration?: number;
526
+ /**
527
+ * `Cache-Control` of a payload file answered by a host or by a path-wide rule, which cannot see `?v=`.
528
+ * On a Node server a response to the current `?v=` is pinned for `httpCacheDuration` and any other revalidates;
529
+ * with `source`, `cacheTtl` or `apiBaseServerHost`, where a dictionary changes with no build, every response takes this header.
530
+ * `false` keeps every file on `max-age=0, must-revalidate`.
531
+ * @default 'public, max-age=60'
532
+ */
533
+ httpCacheControl?: string | false;
489
534
  /**
490
535
  * Value used for cache-busting translation requests (`?v=...`).
491
536
  *
492
- * When not provided, the module falls back to `Date.now()` (non-deterministic).
493
- * For reproducible/rolling deployments, set this to a stable value
494
- * (e.g. a git SHA or build number).
537
+ * When not provided, the module uses a hash of the translation files, so the value changes
538
+ * only when translations do (`Date.now()` when no hash can be computed). `0` or `''` turns
539
+ * cache-busting off. Set a stable value (a git SHA, a build number) to pin it yourself.
540
+ * `NUXT_I18N_DATE_BUILD` or `runtimeConfig.public.i18nRuntime.dateBuild` replaces it at runtime, after a dictionary
541
+ * was replaced in place: pinned URLs are left behind, with no rebuild.
495
542
  *
496
543
  * @example
497
544
  * i18n: { dateBuild: 'e3b0c442' }
@@ -549,6 +596,13 @@ export interface TranslationPayloadOptions {
549
596
  * So static files match client fetch URLs (`/{apiBaseUrl}/{page}/{locale}/data.json`).
550
597
  */
551
598
  publicDir?: string;
599
+ /**
600
+ * Write one root file per locale, and in each page file only what differs from it. The client and SSR merge them;
601
+ * a whole chunk still works wherever a file is read. `false` keeps a whole dictionary in every page file.
602
+ * `premerged` only, and off with `disablePageLocales`.
603
+ * @default true
604
+ */
605
+ rootDelta?: boolean;
552
606
  /**
553
607
  * Warn during build when generated payload file count exceeds this threshold.
554
608
  * @default 500
@@ -569,8 +623,8 @@ export interface ModuleOptionsExtend extends ModuleOptions {
569
623
  dateBuild: string | number;
570
624
  /** Whether the Nuxt app uses hash-based routing (`router.options.hashMode`). */
571
625
  hashMode: boolean;
572
- /** Whether the current build is a static site generation (SSG / `nuxi generate`). */
573
- isSSG: boolean;
626
+ /** @deprecated No longer set at runtime. Kept so code typed against the released module still compiles. */
627
+ isSSG?: boolean;
574
628
  /** Resolved translations API base URL (after env variable / config merging). */
575
629
  apiBaseUrl: string;
576
630
  /** Resolved flag: whether per-page translations are disabled. */
@@ -579,14 +633,30 @@ export interface ModuleOptionsExtend extends ModuleOptions {
579
633
  redirects?: boolean;
580
634
  /** Resolved translation payload loading strategy. */
581
635
  translationPayloadMode?: 'premerged' | 'source';
636
+ /** Premerged files are in `public/`, so they can sit on `app.cdnURL`. */
637
+ publicPayloads?: boolean;
638
+ /** A page's file needs its root: the browser loads the two together. */
639
+ rootDelta?: boolean;
640
+ /** Nuxt's `features.noScripts` is on: no page runs a script, so none preloads a dictionary. */
641
+ noScripts?: boolean;
642
+ /** Host this config was resolved for (`locales[].baseUrl`); unset on any other host. */
643
+ domain?: string;
644
+ /** With `domain`: every locale, those of other domains included. `locales` holds what the host serves. */
645
+ linkLocales?: Locale[];
646
+ /** With `domain`: the build's `defaultLocale`, the same on every host (`x-default`). `defaultLocale` is the host's. */
647
+ linkDefaultLocale?: string;
582
648
  }
649
+ /** The keys `buildFullConfig` always sets: read at runtime with no default of their own. */
650
+ type ResolvedI18nConfigKey = 'apiBaseUrl' | 'autoDetectPath' | 'cacheMaxSize' | 'cacheTtl' | 'canonicalQueryWhitelist' | 'defaultLocale' | 'hreflangBaseLanguage' | 'locales' | 'missingWarn' | 'strategy';
651
+ /** The config the build writes, as `getI18nConfig()` and `$getI18nConfig()` give it. */
652
+ export type ResolvedI18nConfig = Omit<ModuleOptionsExtend, ResolvedI18nConfigKey> & Required<Pick<ModuleOptionsExtend, ResolvedI18nConfigKey>>;
583
653
  /**
584
654
  * Private (server-only) module options passed to Nitro plugins and server middleware.
585
655
  * Extends `ModuleOptions` with resolved root paths and directory info.
586
656
  */
587
657
  export interface ModulePrivateOptionsExtend extends ModuleOptions {
588
- /** Absolute path to the Nuxt project root directory. */
589
- rootDir: string;
658
+ /** Nuxt's `buildId`: `cacheStorage` keys of two builds never meet. */
659
+ cacheStorageBuild?: string;
590
660
  /**
591
661
  * Absolute Nuxt layer roots in merge order (extends first, project last).
592
662
  * Used by live/HMR translation reads so layers match build-time merge.
@@ -596,20 +666,8 @@ export interface ModulePrivateOptionsExtend extends ModuleOptions {
596
666
  debug: boolean;
597
667
  /** Resolved path to the translations directory. */
598
668
  translationDir: string;
599
- /** Resolved fallback locale code. */
600
- fallbackLocale: string;
601
- /** Resolved translations API base URL. */
602
- apiBaseUrl: string;
603
- /** Resolved client-side host override. */
604
- apiBaseClientHost?: string;
605
669
  /** Resolved server-side host override. */
606
670
  apiBaseServerHost?: string;
607
- /** Resolved custom regex matcher (RegExp is serialized to its `.source` string). */
608
- customRegexMatcher?: string | RegExp;
609
- /** Resolved route-to-route translation sharing map. */
610
- routesLocaleLinks?: {
611
- [key: string]: string;
612
- };
613
671
  /** Preload index translations in Nitro global middleware. */
614
672
  serverTranslationPreload?: boolean;
615
673
  /**
@@ -673,3 +731,16 @@ export interface I18nHeadInput {
673
731
  }
674
732
  declare const init: () => void;
675
733
  export { init };
734
+ /** `meta.i18n` that route-strategy puts on every localized record, and path-strategy reads at runtime. */
735
+ export interface RouteI18nMeta {
736
+ /** Base route name of the page (its translation key and the name `localeRoute({ name })` takes). */
737
+ name: string;
738
+ /** Locales this record serves. */
739
+ locales: string[];
740
+ /** `disableMeta` of the page: SEO tags off in every locale (`true`) or in the listed ones. */
741
+ noMeta?: boolean | string[];
742
+ /** On the default locale's record: the path of a page with a custom default path, where it is no record of its own. A link can still name the page by it. */
743
+ original?: string;
744
+ /** `noPrefixRedirect`, on the page's original path: the custom path of each locale. The locale is the visitor's, so the runtime redirects. */
745
+ paths?: Record<string, string>;
746
+ }
package/dist/index.mjs CHANGED
@@ -1,4 +1,3 @@
1
- //#region src/index.ts
1
+ // src/index.ts
2
2
  var e = () => {};
3
- //#endregion
4
3
  export { e as init };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@i18n-micro/types",
3
- "version": "1.2.18",
3
+ "version": "2.0.0",
4
4
  "description": "TypeScript definitions for the Nuxt I18n Micro ecosystem.",
5
5
  "keywords": [
6
6
  "i18n",
@@ -30,8 +30,8 @@
30
30
  ],
31
31
  "type": "module",
32
32
  "sideEffects": false,
33
- "main": "dist/index.cjs",
34
- "types": "dist/index.d.ts",
33
+ "main": "./dist/index.cjs",
34
+ "types": "./dist/index.d.ts",
35
35
  "exports": {
36
36
  ".": {
37
37
  "import": {
@@ -50,13 +50,13 @@
50
50
  "access": "public"
51
51
  },
52
52
  "devDependencies": {
53
- "publint": "^0.3.24",
54
- "vite": "^8.3.1",
55
- "vite-plugin-dts": "^5.1.1",
56
- "vitest": "^5.0.2"
53
+ "publint": "^0.3.25",
54
+ "vite": "^8.3.5",
55
+ "vite-plugin-dts": "^5.1.2",
56
+ "vitest": "^5.1.0"
57
57
  },
58
58
  "engines": {
59
- "node": ">=18"
59
+ "node": ">=20"
60
60
  },
61
61
  "scripts": {
62
62
  "build": "vite build",