@bleedingdev/modern-js-plugin-i18n 3.5.0-ultramodern.36 → 3.5.0-ultramodern.37

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.
Files changed (91) hide show
  1. package/dist/cjs/cli/locales.js +15 -15
  2. package/dist/cjs/runtime/i18n/backend/defaults.node.js +7 -7
  3. package/dist/cjs/runtime/i18n/detection/cache.js +3 -2
  4. package/dist/cjs/runtime/i18n/instance.js +7 -7
  5. package/dist/cjs/runtime/utils.js +1 -9
  6. package/dist/cjs/server/redirectPolicy.js +14 -20
  7. package/dist/cjs/shared/detection.js +2 -104
  8. package/dist/cjs/shared/localisedUrls/index.js +16 -0
  9. package/dist/cjs/shared/localisedUrls/pathname.js +5 -2
  10. package/dist/cjs/shared/localisedUrls/redirect.js +95 -0
  11. package/dist/cjs/shared/localisedUrls/resolve.js +29 -32
  12. package/dist/cjs/shared/localisedUrls.js +15 -0
  13. package/dist/cjs/shared/utils.js +2 -8
  14. package/dist/esm/cli/locales.mjs +13 -13
  15. package/dist/esm/runtime/i18n/backend/defaults.node.mjs +5 -5
  16. package/dist/esm/runtime/i18n/detection/cache.mjs +3 -2
  17. package/dist/esm/runtime/i18n/instance.mjs +8 -5
  18. package/dist/esm/runtime/utils.mjs +2 -10
  19. package/dist/esm/server/index.mjs +2 -2
  20. package/dist/esm/server/redirectPolicy.mjs +15 -21
  21. package/dist/esm/shared/detection.mjs +1 -71
  22. package/dist/esm/shared/localisedUrls/index.mjs +2 -1
  23. package/dist/esm/shared/localisedUrls/pathname.mjs +1 -1
  24. package/dist/esm/shared/localisedUrls/redirect.mjs +48 -0
  25. package/dist/esm/shared/localisedUrls/resolve.mjs +29 -32
  26. package/dist/esm/shared/localisedUrls.mjs +1 -1
  27. package/dist/esm/shared/utils.mjs +1 -1
  28. package/dist/esm-node/cli/locales.mjs +13 -13
  29. package/dist/esm-node/runtime/i18n/backend/defaults.node.mjs +5 -5
  30. package/dist/esm-node/runtime/i18n/detection/cache.mjs +3 -2
  31. package/dist/esm-node/runtime/i18n/instance.mjs +8 -5
  32. package/dist/esm-node/runtime/utils.mjs +2 -10
  33. package/dist/esm-node/server/index.mjs +2 -2
  34. package/dist/esm-node/server/redirectPolicy.mjs +15 -21
  35. package/dist/esm-node/shared/detection.mjs +1 -71
  36. package/dist/esm-node/shared/localisedUrls/index.mjs +2 -1
  37. package/dist/esm-node/shared/localisedUrls/pathname.mjs +1 -1
  38. package/dist/esm-node/shared/localisedUrls/redirect.mjs +49 -0
  39. package/dist/esm-node/shared/localisedUrls/resolve.mjs +29 -32
  40. package/dist/esm-node/shared/localisedUrls.mjs +1 -1
  41. package/dist/esm-node/shared/utils.mjs +1 -1
  42. package/dist/types/cli/locales.d.ts +1 -1
  43. package/dist/types/runtime/context.d.ts +4 -3
  44. package/dist/types/runtime/contextHelpers.d.ts +2 -2
  45. package/dist/types/runtime/i18n/backend/middleware.common.d.ts +2 -1
  46. package/dist/types/runtime/i18n/detection/cache.d.ts +1 -1
  47. package/dist/types/runtime/i18n/detection/detector.d.ts +2 -2
  48. package/dist/types/runtime/i18n/detection/middleware.d.ts +4 -4
  49. package/dist/types/runtime/i18n/detection/middleware.node.d.ts +4 -4
  50. package/dist/types/runtime/i18n/detection/types.d.ts +8 -5
  51. package/dist/types/runtime/i18n/instance.d.ts +28 -26
  52. package/dist/types/runtime/i18n/utils.d.ts +14 -5
  53. package/dist/types/runtime/pluginSetup.d.ts +1 -1
  54. package/dist/types/runtime/providerComposition.d.ts +2 -1
  55. package/dist/types/runtime/routerAdapter.d.ts +7 -6
  56. package/dist/types/server/redirectPolicy.d.ts +14 -8
  57. package/dist/types/shared/detection.d.ts +1 -11
  58. package/dist/types/shared/localisedUrls/index.d.ts +3 -1
  59. package/dist/types/shared/localisedUrls/pathname.d.ts +1 -0
  60. package/dist/types/shared/localisedUrls/redirect.d.ts +11 -0
  61. package/dist/types/shared/localisedUrls/resolve.d.ts +2 -3
  62. package/dist/types/shared/localisedUrls.d.ts +2 -2
  63. package/dist/types/shared/utils.d.ts +0 -2
  64. package/package.json +16 -18
  65. package/src/cli/index.ts +2 -2
  66. package/src/cli/locales.ts +3 -3
  67. package/src/runtime/context.tsx +3 -3
  68. package/src/runtime/contextHelpers.ts +3 -3
  69. package/src/runtime/hooks.ts +10 -2
  70. package/src/runtime/i18n/backend/defaults.node.ts +2 -2
  71. package/src/runtime/i18n/backend/middleware.common.ts +5 -4
  72. package/src/runtime/i18n/detection/cache.ts +23 -9
  73. package/src/runtime/i18n/detection/detector.ts +15 -6
  74. package/src/runtime/i18n/detection/middleware.node.ts +26 -7
  75. package/src/runtime/i18n/detection/middleware.ts +15 -7
  76. package/src/runtime/i18n/detection/types.ts +13 -5
  77. package/src/runtime/i18n/instance.ts +56 -31
  78. package/src/runtime/i18n/utils.ts +18 -9
  79. package/src/runtime/pluginSetup.ts +1 -1
  80. package/src/runtime/providerComposition.tsx +1 -1
  81. package/src/runtime/routerAdapter.tsx +6 -6
  82. package/src/runtime/utils.ts +5 -29
  83. package/src/server/redirectPolicy.ts +51 -76
  84. package/src/shared/detection.ts +1 -1
  85. package/src/shared/localisedUrls/index.ts +12 -1
  86. package/src/shared/localisedUrls/pathname.ts +4 -1
  87. package/src/shared/localisedUrls/redirect.ts +91 -0
  88. package/src/shared/localisedUrls/resolve.ts +91 -74
  89. package/src/shared/localisedUrls.ts +6 -0
  90. package/src/shared/utils.ts +2 -2
  91. package/tests/redirectPolicy.test.ts +43 -0
@@ -1,46 +1,48 @@
1
1
  import type { BaseBackendOptions } from '../../shared/type';
2
- export interface I18nResourceStore {
2
+ interface I18nResourceStore {
3
3
  data?: {
4
4
  [language: string]: {
5
- [namespace: string]: string | {
6
- [key: string]: any;
7
- };
5
+ [namespace: string]: ResourceValue;
8
6
  };
9
7
  };
10
8
  addResourceBundle?: (language: string, namespace: string, resources: Record<string, string>, deep?: boolean, overwrite?: boolean) => void;
11
9
  }
12
- export declare function isI18nWrapperInstance(obj: any): boolean;
13
- export declare function getI18nWrapperI18nextInstance(wrapperInstance: any): any;
14
- export declare function getActualI18nextInstance(instance: I18nInstance | any): any;
10
+ type I18nWrapperInstance = I18nInstance & {
11
+ i18nInstance: {
12
+ instance: I18nInstance;
13
+ };
14
+ };
15
+ export declare function isI18nWrapperInstance(obj: unknown): obj is I18nWrapperInstance;
16
+ export declare function getActualI18nextInstance(instance: I18nInstance): I18nInstance;
15
17
  export interface I18nInstance {
16
18
  language: string;
17
19
  isInitialized?: boolean;
18
20
  init: {
19
- (callback?: (error: any, t: any) => void): Promise<any>;
20
- (options: I18nInitOptions, callback?: (error: any, t: any) => void): Promise<any>;
21
+ (callback?: (error: unknown, t: unknown) => void): Promise<unknown>;
22
+ (options: I18nInitOptions, callback?: (error: unknown, t: unknown) => void): Promise<unknown>;
21
23
  };
22
- changeLanguage?: (lng?: string, callback?: (error: any, t: any) => void) => Promise<any>;
24
+ changeLanguage?: (lng?: string, callback?: (error: unknown, t: unknown) => void) => Promise<unknown>;
23
25
  setLang?: (lang: string) => void | Promise<void>;
24
- use: (plugin: any) => void;
26
+ use: (plugin: unknown) => void;
25
27
  createInstance?: (options?: I18nInitOptions) => I18nInstance;
26
28
  cloneInstance?: () => I18nInstance;
27
29
  store?: I18nResourceStore;
28
- emit?: (event: string, ...args: any[]) => void;
30
+ emit?: (event: string, ...args: unknown[]) => void;
29
31
  reloadResources?: (language?: string, namespace?: string) => Promise<void>;
30
32
  services?: {
31
33
  languageDetector?: {
32
- detect: (request?: any, options?: any) => string | string[] | undefined;
33
- [key: string]: any;
34
+ detect: (request?: unknown, options?: unknown) => string | string[] | undefined;
35
+ [key: string]: unknown;
34
36
  };
35
37
  resourceStore?: I18nResourceStore;
36
- backend?: any;
37
- [key: string]: any;
38
+ backend?: unknown;
39
+ [key: string]: unknown;
38
40
  };
39
41
  options?: {
40
42
  backend?: BackendOptions;
41
- [key: string]: any;
43
+ [key: string]: unknown;
42
44
  };
43
- [key: string]: any;
45
+ [key: string]: unknown;
44
46
  }
45
47
  type LanguageDetectorOrder = string[];
46
48
  type LanguageDetectorCaches = boolean | string[];
@@ -57,11 +59,11 @@ export interface LanguageDetectorOptions {
57
59
  lookupHeader?: string;
58
60
  }
59
61
  export interface BackendOptions extends Omit<BaseBackendOptions, 'enabled'> {
60
- parse?: (data: string) => any;
61
- stringify?: (data: any) => string;
62
+ parse?: (data: string) => unknown;
63
+ stringify?: (data: unknown) => string;
62
64
  [key: string]: any;
63
65
  }
64
- export type ResourceValue = string | {
66
+ type ResourceValue = string | {
65
67
  [key: string]: ResourceValue;
66
68
  };
67
69
  export interface Resources {
@@ -81,14 +83,14 @@ export type I18nInitOptions = {
81
83
  defaultNS?: string | string[];
82
84
  interpolation?: {
83
85
  escapeValue?: boolean;
84
- [key: string]: any;
86
+ [key: string]: unknown;
85
87
  };
86
88
  react?: {
87
89
  useSuspense?: boolean;
88
- [key: string]: any;
90
+ [key: string]: unknown;
89
91
  };
90
92
  };
91
- export declare function isI18nInstance(obj: any): obj is I18nInstance;
92
- export declare function getI18nextInstanceForProvider(instance: I18nInstance | any): any;
93
- export declare function getI18nInstance(userInstance?: I18nInstance | any): Promise<I18nInstance>;
93
+ export declare function isI18nInstance(obj: unknown): obj is I18nInstance;
94
+ export declare function getI18nextInstanceForProvider(instance: I18nInstance): I18nInstance;
95
+ export declare function getI18nInstance(userInstance?: unknown): Promise<I18nInstance>;
94
96
  export {};
@@ -1,10 +1,18 @@
1
1
  import type { BaseBackendOptions } from '../../shared/type';
2
2
  import type { I18nInitOptions, I18nInstance } from './instance';
3
- export declare function assertI18nInstance(obj: any): asserts obj is I18nInstance;
3
+ type MergedBackendOptions = NonNullable<I18nInitOptions['backend']> & {
4
+ _useChainedBackend?: boolean;
5
+ _chainedBackendConfig?: {
6
+ backendOptions: Array<Record<string, unknown>>;
7
+ };
8
+ backends?: unknown[];
9
+ backendOptions?: unknown;
10
+ };
11
+ export declare function assertI18nInstance(obj: unknown): asserts obj is I18nInstance;
4
12
  /**
5
13
  * Build initialization options for i18n instance
6
14
  */
7
- export declare const buildInitOptions: (finalLanguage: string, fallbackLanguage: string, languages: string[], mergedDetection: any, mergedBackend: any, userInitOptions?: I18nInitOptions, useSuspense?: boolean, i18nInstance?: I18nInstance) => Promise<I18nInitOptions>;
15
+ export declare const buildInitOptions: (finalLanguage: string, fallbackLanguage: string, languages: string[], mergedDetection: I18nInitOptions['detection'], mergedBackend: MergedBackendOptions | undefined, userInitOptions?: I18nInitOptions, useSuspense?: boolean, i18nInstance?: I18nInstance) => Promise<I18nInitOptions>;
8
16
  /**
9
17
  * Ensure i18n instance language matches the final detected language
10
18
  */
@@ -17,13 +25,14 @@ export declare const ensureLanguageMatch: (i18nInstance: I18nInstance, finalLang
17
25
  * @param options - Optional configuration
18
26
  */
19
27
  export declare const changeI18nLanguage: (i18nInstance: I18nInstance, newLang: string, options?: {
20
- detectionOptions?: any;
28
+ detectionOptions?: I18nInitOptions['detection'];
21
29
  }) => Promise<void>;
22
30
  /**
23
31
  * Initialize i18n instance if not already initialized
24
32
  */
25
- export declare const initializeI18nInstance: (i18nInstance: I18nInstance, finalLanguage: string, fallbackLanguage: string, languages: string[], mergedDetection: any, mergedBackend: any, userInitOptions?: I18nInitOptions, useSuspense?: boolean) => Promise<void>;
33
+ export declare const initializeI18nInstance: (i18nInstance: I18nInstance, finalLanguage: string, fallbackLanguage: string, languages: string[], mergedDetection: I18nInitOptions['detection'], mergedBackend: MergedBackendOptions | undefined, userInitOptions?: I18nInitOptions, useSuspense?: boolean) => Promise<void>;
26
34
  /**
27
35
  * Setup cloned instance for SSR with backend support
28
36
  */
29
- export declare const setupClonedInstance: (i18nInstance: I18nInstance, finalLanguage: string, fallbackLanguage: string, languages: string[], backendEnabled: boolean, backend: BaseBackendOptions | undefined, i18nextDetector: boolean, detection: any, localePathRedirect: boolean, userInitOptions: I18nInitOptions | undefined) => Promise<void>;
37
+ export declare const setupClonedInstance: (i18nInstance: I18nInstance, finalLanguage: string, fallbackLanguage: string, languages: string[], backendEnabled: boolean, backend: BaseBackendOptions | undefined, i18nextDetector: boolean, detection: I18nInitOptions['detection'], localePathRedirect: boolean, userInitOptions: I18nInitOptions | undefined) => Promise<void>;
38
+ export {};
@@ -14,7 +14,7 @@ export interface RuntimeContextWithI18n extends TInternalRuntimeContext {
14
14
  i18nInstance?: I18nInstance;
15
15
  changeLanguage?: (lang: string) => Promise<void>;
16
16
  }
17
- export interface SetupI18nBeforeRenderOptions {
17
+ interface SetupI18nBeforeRenderOptions {
18
18
  api: I18nRuntimeApi;
19
19
  userI18nInstance?: I18nInstance;
20
20
  initOptions?: I18nInitOptions;
@@ -1,7 +1,7 @@
1
1
  import type React from 'react';
2
2
  import type { BaseLocaleDetectionOptions } from '../shared/type';
3
3
  import type { I18nInstance } from './i18n';
4
- export interface I18nRootWrapperOptions {
4
+ interface I18nRootWrapperOptions {
5
5
  entryName?: string;
6
6
  htmlLangAttr: boolean;
7
7
  localePathRedirect: boolean;
@@ -13,3 +13,4 @@ export interface I18nRootWrapperOptions {
13
13
  getI18nextProvider: () => React.ComponentType<any> | null | undefined;
14
14
  }
15
15
  export declare const createI18nRootWrapper: (options: I18nRootWrapperOptions) => (App: React.ComponentType<any>) => (props: Record<string, unknown>) => React.JSX.Element;
16
+ export {};
@@ -1,21 +1,21 @@
1
1
  import type React from 'react';
2
- export type I18nRouterFramework = 'react-router' | 'tanstack' | string;
3
- export interface I18nRouterLocation {
2
+ type I18nRouterFramework = 'react-router' | 'tanstack' | string;
3
+ interface I18nRouterLocation {
4
4
  pathname: string;
5
5
  search: string;
6
6
  hash: string;
7
7
  }
8
- export interface I18nRouterNavigateOptions {
8
+ interface I18nRouterNavigateOptions {
9
9
  replace?: boolean;
10
10
  state?: unknown;
11
11
  }
12
- export type I18nRouterNavigate = (href: string, options?: I18nRouterNavigateOptions) => void | Promise<void>;
13
- export type I18nRouterLink = React.ComponentType<{
12
+ type I18nRouterNavigate = (href: string, options?: I18nRouterNavigateOptions) => void | Promise<void>;
13
+ type I18nRouterLink = React.ComponentType<{
14
14
  to: string;
15
15
  children?: React.ReactNode;
16
16
  [key: string]: unknown;
17
17
  }>;
18
- export interface I18nRouterAdapter {
18
+ interface I18nRouterAdapter {
19
19
  framework?: I18nRouterFramework;
20
20
  hasRouter: boolean;
21
21
  location: I18nRouterLocation | null;
@@ -24,3 +24,4 @@ export interface I18nRouterAdapter {
24
24
  params: Record<string, string>;
25
25
  }
26
26
  export declare const useI18nRouterAdapter: () => I18nRouterAdapter;
27
+ export {};
@@ -1,16 +1,22 @@
1
1
  import type { LocaleDetectionOptions } from '../shared/type';
2
+ interface LocaleRedirectRequest {
3
+ url: string;
4
+ header: () => {
5
+ host?: string;
6
+ };
7
+ }
2
8
  /**
3
- * Check if the given pathname should ignore automatic locale redirect
9
+ * Check if pathname should ignore automatic locale redirect.
4
10
  */
5
11
  export declare const shouldIgnoreRedirect: (pathname: string, urlPath: string, ignoreRedirectRoutes?: string[] | ((pathname: string) => boolean)) => boolean;
6
12
  /**
7
- * Check if the given pathname is a static resource request
8
- * This includes:
9
- * 1. Paths matching staticRoutePrefixes (from public directories)
10
- * 2. Standard static resource paths like /static/, /upload/
11
- * 3. Paths with language prefix like /en/static/, /zh/static/
13
+ * Check if pathname is a static or federation resource request.
14
+ *
15
+ * This includes configured staticRoutePrefixes, the shared default skip policy
16
+ * from ADR-0002, and language-prefixed variants such as /en/static/app.js.
12
17
  */
13
18
  export declare const isStaticResourceRequest: (pathname: string, staticRoutePrefixes: string[], languages?: string[]) => boolean;
14
- export declare const getLanguageFromPath: (req: any, urlPath: string, languages: string[]) => string | null;
15
- export declare const buildLocalizedUrl: (req: any, urlPath: string, language: string, languages: string[], localisedUrls?: LocaleDetectionOptions['localisedUrls']) => string;
19
+ export declare const getLanguageFromPath: (req: LocaleRedirectRequest, urlPath: string, languages: string[]) => string | null;
20
+ export declare const buildLocalizedUrl: (req: LocaleRedirectRequest, urlPath: string, language: string, languages: string[], localisedUrls?: LocaleDetectionOptions['localisedUrls']) => string;
16
21
  export declare const createLocaleRedirectResponse: (location: string) => Response;
22
+ export {};
@@ -1,11 +1 @@
1
- import type { LanguageDetectorOptions } from '../runtime/i18n/instance';
2
- /**
3
- * Detect language from request using the same detection logic as i18next
4
- * This ensures consistency between server-side and client-side detection
5
- */
6
- export declare function detectLanguageFromRequest(req: {
7
- url: string;
8
- headers: {
9
- get: (name: string) => string | null;
10
- } | Headers;
11
- }, languages: string[], detectionOptions?: LanguageDetectorOptions): string | null;
1
+ export {};
@@ -1,7 +1,9 @@
1
1
  export { resolveLocalisedUrlsConfig } from './config';
2
2
  export { normalisePathname, normalisePathPattern } from './normalise';
3
- export { canonicalTargetPathname, localiseTargetPathname } from './pathname';
3
+ export { canonicalTargetPathname, localiseTargetPathname, stripLanguagePrefix, } from './pathname';
4
4
  export { buildPathFromPattern, matchPathPattern } from './patterns';
5
+ export type { LocaleRedirectSkipRule } from './redirect';
6
+ export { DEFAULT_LOCALE_REDIRECT_SKIP_RULES, isDefaultLocaleRedirectSkipPath, matchesPathPrefix, shouldSkipLocaleRedirect, } from './redirect';
5
7
  export { resolveCanonicalLocalisedPath, resolveLocalisedPath } from './resolve';
6
8
  export { applyLocalisedUrlsToRoutes, validateLocalisedUrls } from './routes';
7
9
  export type { LocalisedRoute, LocalisedUrlPathMap, LocalisedUrlsMap, LocalisedUrlsOption, ResolvedLocalisedUrlsConfig, } from './types';
@@ -1,3 +1,4 @@
1
1
  import type { LocalisedUrlsOption } from './types';
2
+ export declare const stripLanguagePrefix: (pathname: string, languages: string[]) => string;
2
3
  export declare const localiseTargetPathname: (pathname: string, language: string, languages: string[], localisedUrls?: LocalisedUrlsOption) => string;
3
4
  export declare const canonicalTargetPathname: (pathname: string, languages: string[], localisedUrls?: LocalisedUrlsOption) => string;
@@ -0,0 +1,11 @@
1
+ export type LocaleRedirectSkipRule = {
2
+ type: 'exact';
3
+ path: string;
4
+ } | {
5
+ type: 'prefix';
6
+ path: string;
7
+ };
8
+ export declare const DEFAULT_LOCALE_REDIRECT_SKIP_RULES: readonly LocaleRedirectSkipRule[];
9
+ export declare const matchesPathPrefix: (pathname: string, prefix: string) => boolean;
10
+ export declare const isDefaultLocaleRedirectSkipPath: (pathname: string, languages?: string[]) => boolean;
11
+ export declare const shouldSkipLocaleRedirect: (pathname: string, languages?: string[], ignoreRedirectRoutes?: string[] | ((pathname: string) => boolean)) => boolean;
@@ -1,8 +1,7 @@
1
1
  import type { LocalisedUrlsMap } from './types';
2
2
  export declare const resolveLocalisedPath: (pathname: string, targetLanguage: string, languages: string[], localisedUrls: LocalisedUrlsMap) => string;
3
3
  /**
4
- * Reverse-map a language-specific pathname (without language prefix) back to
5
- * the canonical, language-agnostic path: localized slug patterns are matched
6
- * against every language variant and rebuilt from the canonical map key.
4
+ * Reverse-map language-specific pathnames back to language-agnostic
5
+ * LocalisedUrlsMap keys.
7
6
  */
8
7
  export declare const resolveCanonicalLocalisedPath: (pathname: string, languages: string[], localisedUrls: LocalisedUrlsMap) => string;
@@ -1,2 +1,2 @@
1
- export type { LocalisedRoute, LocalisedUrlPathMap, LocalisedUrlsMap, LocalisedUrlsOption, ResolvedLocalisedUrlsConfig, } from './localisedUrls/index';
2
- export { applyLocalisedUrlsToRoutes, buildPathFromPattern, canonicalTargetPathname, localiseTargetPathname, matchPathPattern, normalisePathname, normalisePathPattern, resolveCanonicalLocalisedPath, resolveLocalisedPath, resolveLocalisedUrlsConfig, validateLocalisedUrls, } from './localisedUrls/index';
1
+ export type { LocaleRedirectSkipRule, LocalisedRoute, LocalisedUrlPathMap, LocalisedUrlsMap, LocalisedUrlsOption, ResolvedLocalisedUrlsConfig, } from './localisedUrls/index';
2
+ export { applyLocalisedUrlsToRoutes, buildPathFromPattern, canonicalTargetPathname, DEFAULT_LOCALE_REDIRECT_SKIP_RULES, isDefaultLocaleRedirectSkipPath, localiseTargetPathname, matchesPathPrefix, matchPathPattern, normalisePathname, normalisePathPattern, resolveCanonicalLocalisedPath, resolveLocalisedPath, resolveLocalisedUrlsConfig, shouldSkipLocaleRedirect, stripLanguagePrefix, validateLocalisedUrls, } from './localisedUrls/index';
@@ -1,5 +1,3 @@
1
1
  import type { BaseBackendOptions, BaseLocaleDetectionOptions } from './type';
2
- export declare function getEntryConfig<T extends Record<string, any>>(entryName: string, config: T, entryKey: string): T | undefined;
3
- export declare function removeEntryConfigKey<T extends Record<string, any>>(config: T, entryKey: string): Omit<T, typeof entryKey>;
4
2
  export declare function getLocaleDetectionOptions(entryName: string, localeDetection: BaseLocaleDetectionOptions): BaseLocaleDetectionOptions;
5
3
  export declare function getBackendOptions(entryName: string, backend: BaseBackendOptions): BaseBackendOptions;
package/package.json CHANGED
@@ -17,7 +17,7 @@
17
17
  "modern",
18
18
  "modern.js"
19
19
  ],
20
- "version": "3.5.0-ultramodern.36",
20
+ "version": "3.5.0-ultramodern.37",
21
21
  "engines": {
22
22
  "node": ">=20"
23
23
  },
@@ -97,19 +97,18 @@
97
97
  "i18next-fs-backend": "^2.6.6",
98
98
  "i18next-http-backend": "^4.0.0",
99
99
  "i18next-http-middleware": "^3.9.7",
100
- "@modern-js/server-core": "npm:@bleedingdev/modern-js-server-core@3.5.0-ultramodern.36",
101
- "@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.5.0-ultramodern.36",
102
- "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.5.0-ultramodern.36",
103
- "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.5.0-ultramodern.36",
104
- "@modern-js/server-runtime": "npm:@bleedingdev/modern-js-server-runtime@3.5.0-ultramodern.36",
105
- "@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.5.0-ultramodern.36"
100
+ "@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.5.0-ultramodern.37",
101
+ "@modern-js/server-runtime": "npm:@bleedingdev/modern-js-server-runtime@3.5.0-ultramodern.37",
102
+ "@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.5.0-ultramodern.37",
103
+ "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.5.0-ultramodern.37",
104
+ "@modern-js/server-core": "npm:@bleedingdev/modern-js-server-core@3.5.0-ultramodern.37"
106
105
  },
107
106
  "peerDependencies": {
108
- "@modern-js/runtime": "3.5.0-ultramodern.36",
107
+ "@modern-js/runtime": "3.5.0-ultramodern.37",
109
108
  "i18next": ">=26.3.1",
110
109
  "react": "^19.2.7",
111
110
  "react-dom": "^19.2.7",
112
- "react-i18next": "^17.0.8"
111
+ "react-i18next": "^17.0.9"
113
112
  },
114
113
  "peerDependenciesMeta": {
115
114
  "i18next": {
@@ -120,17 +119,16 @@
120
119
  }
121
120
  },
122
121
  "devDependencies": {
123
- "@rslib/core": "0.23.1",
124
- "@types/node": "^26.0.1",
125
- "@typescript/native-preview": "7.0.0-dev.20260628.1",
126
- "i18next": "26.3.3",
122
+ "@rslib/core": "0.23.2",
123
+ "@types/node": "^26.1.1",
124
+ "@typescript/native-preview": "7.0.0-dev.20260707.2",
125
+ "i18next": "26.3.6",
127
126
  "react": "^19.2.7",
128
127
  "react-dom": "^19.2.7",
129
- "react-i18next": "17.0.8",
130
- "ts-node": "^10.9.2",
131
- "typescript": "^6.0.3",
132
- "@modern-js/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.5.0-ultramodern.36",
133
- "@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.5.0-ultramodern.36"
128
+ "react-i18next": "17.0.9",
129
+ "typescript": "^7.0.2",
130
+ "@modern-js/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.5.0-ultramodern.37",
131
+ "@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.5.0-ultramodern.37"
134
132
  },
135
133
  "sideEffects": false,
136
134
  "publishConfig": {
package/src/cli/index.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
1
3
  import type { AppTools, CliPlugin } from '@modern-js/app-tools';
2
4
  import { getPublicDirRoutePrefixes } from '@modern-js/server-core';
3
5
  import type {
@@ -5,8 +7,6 @@ import type {
5
7
  NestedRouteForCli,
6
8
  PageRoute,
7
9
  } from '@modern-js/types';
8
- import fs from 'fs';
9
- import path from 'path';
10
10
  import {
11
11
  applyLocalisedUrlsToRoutes,
12
12
  resolveLocalisedUrlsConfig,
@@ -1,10 +1,10 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
1
3
  import {
2
4
  getPublicDirRoutePrefixes,
3
5
  normalizePublicDir,
4
6
  normalizePublicDirPath,
5
7
  } from '@modern-js/server-core';
6
- import fs from 'fs';
7
- import path from 'path';
8
8
  import type { BackendOptions } from '../shared/type';
9
9
 
10
10
  interface NormalizedConfigForLocales {
@@ -13,7 +13,7 @@ interface NormalizedConfigForLocales {
13
13
  };
14
14
  }
15
15
 
16
- export interface DetectedLocalesDirectory {
16
+ interface DetectedLocalesDirectory {
17
17
  loadPath: string;
18
18
  addPath: string;
19
19
  serverLoadPath: string;
@@ -18,7 +18,7 @@ import {
18
18
  import type { I18nInstance } from './i18n';
19
19
  import { useI18nRouterAdapter } from './routerAdapter';
20
20
 
21
- export interface ModernI18nContextValue {
21
+ interface ModernI18nContextValue {
22
22
  language: string;
23
23
  i18nInstance: I18nInstance;
24
24
  // Plugin configuration for useModernI18n hook
@@ -50,7 +50,7 @@ const getModernI18nContext = () => {
50
50
 
51
51
  const ModernI18nContext = getModernI18nContext();
52
52
 
53
- export interface ModernI18nProviderProps {
53
+ interface ModernI18nProviderProps {
54
54
  children: ReactNode;
55
55
  value: ModernI18nContextValue;
56
56
  }
@@ -66,7 +66,7 @@ export const ModernI18nProvider: FC<ModernI18nProviderProps> = ({
66
66
  );
67
67
  };
68
68
 
69
- export interface UseModernI18nReturn {
69
+ interface UseModernI18nReturn {
70
70
  language: string;
71
71
  changeLanguage: (newLang: string) => Promise<void>;
72
72
  t: (key: string | string[], ...args: any[]) => string;
@@ -50,7 +50,7 @@ export function cacheI18nLanguage(
50
50
  }
51
51
  }
52
52
 
53
- export interface ChangeModernI18nLanguageOptions {
53
+ interface ChangeModernI18nLanguageOptions {
54
54
  i18nInstance: I18nInstance;
55
55
  updateLanguage?: (newLang: string) => void;
56
56
  localePathRedirect?: boolean;
@@ -172,7 +172,7 @@ export async function changeModernI18nLanguage(
172
172
  export function translateI18n(
173
173
  i18nInstance: I18nInstance,
174
174
  key: string | string[],
175
- ...args: any[]
175
+ ...args: unknown[]
176
176
  ): string {
177
177
  if (typeof i18nInstance.t !== 'function') {
178
178
  throw new Error('i18nInstance.t required');
@@ -207,7 +207,7 @@ export function isI18nResourcesReady(
207
207
  }
208
208
  }
209
209
 
210
- const store = (i18nInstance as any).store;
210
+ const store = i18nInstance.store;
211
211
  if (!store?.data) {
212
212
  return false;
213
213
  }
@@ -26,6 +26,14 @@ interface RuntimeContextWithI18n extends TRuntimeContext {
26
26
  ssrContext?: { request?: { pathname?: string } };
27
27
  }
28
28
 
29
+ type WindowWithSSRData = Window & {
30
+ _SSR_DATA?: unknown;
31
+ };
32
+
33
+ type ResourceStoreWithEvents = NonNullable<I18nInstance['store']> & {
34
+ emit?: (event: string, ...args: unknown[]) => void;
35
+ };
36
+
29
37
  function createMinimalI18nInstance(language: string): I18nInstance {
30
38
  const minimalInstance: I18nInstance = {
31
39
  language,
@@ -97,7 +105,7 @@ export function useSdkResourcesLoader(
97
105
  }
98
106
 
99
107
  const triggerUpdate = (retryCount = 0) => {
100
- const store = (i18nInstance as any).store;
108
+ const store = i18nInstance.store as ResourceStoreWithEvents | undefined;
101
109
  const hasResource = store?.data?.[language]?.[namespace];
102
110
 
103
111
  if (hasResource || retryCount >= 10) {
@@ -182,7 +190,7 @@ export function useClientSideRedirect(
182
190
  }
183
191
 
184
192
  try {
185
- const ssrData = (window as any)._SSR_DATA;
193
+ const ssrData = (window as WindowWithSSRData)._SSR_DATA;
186
194
  if (ssrData) {
187
195
  return;
188
196
  }
@@ -1,5 +1,5 @@
1
- import fs from 'fs';
2
- import nodePath from 'path';
1
+ import fs from 'node:fs';
2
+ import nodePath from 'node:path';
3
3
 
4
4
  /**
5
5
  * Conventional locales roots, in the same priority order as the CLI plugin's
@@ -9,6 +9,7 @@ import { SdkBackend } from './sdk-backend';
9
9
 
10
10
  type BackendConfigWithChained = BaseBackendOptions &
11
11
  Partial<ChainedBackendConfig>;
12
+ type BackendConstructor = new (...args: never[]) => unknown;
12
13
 
13
14
  function checkBackendConfig(backend?: BackendConfigWithChained) {
14
15
  const hasSdk = backend?.sdk && typeof backend.sdk === 'function';
@@ -20,7 +21,7 @@ function checkBackendConfig(backend?: BackendConfigWithChained) {
20
21
 
21
22
  function buildChainedBackendConfig(
22
23
  backend: BackendConfigWithChained,
23
- BackendWithSave: new (...args: any[]) => any,
24
+ BackendWithSave: BackendConstructor,
24
25
  ) {
25
26
  const cacheHitMode = backend.cacheHitMode || 'refreshAndUpdateStore';
26
27
 
@@ -51,7 +52,7 @@ function buildChainedBackendConfig(
51
52
  function setupChainedBackend(
52
53
  i18nInstance: I18nInstance,
53
54
  backend: BackendConfigWithChained,
54
- BackendWithSave: new (...args: any[]) => any,
55
+ BackendWithSave: BackendConstructor,
55
56
  ) {
56
57
  i18nInstance.use(ChainedBackend);
57
58
  const actualInstance = getActualI18nextInstance(i18nInstance);
@@ -86,8 +87,8 @@ function cleanBackendConfig(backend: BackendConfigWithChained) {
86
87
  */
87
88
  export function useI18nextBackendCommon(
88
89
  i18nInstance: I18nInstance,
89
- BackendWithSave: new (...args: any[]) => any,
90
- BackendBase: new (...args: any[]) => any,
90
+ BackendWithSave: BackendConstructor,
91
+ BackendBase: BackendConstructor,
91
92
  backend?: BackendConfigWithChained,
92
93
  ) {
93
94
  if (!backend) {
@@ -10,6 +10,19 @@ interface DetectorCacheEntry {
10
10
  configKey: string;
11
11
  }
12
12
 
13
+ type DetectorCleanupInstance = I18nInstance & {
14
+ removeAllListeners?: () => void;
15
+ off?: (event: string) => void;
16
+ services?: I18nInstance['services'] & {
17
+ backendConnector?: {
18
+ backend?: {
19
+ stop?: () => void;
20
+ close?: () => void;
21
+ };
22
+ };
23
+ };
24
+ };
25
+
13
26
  export const detectorInstanceCache = new WeakMap<
14
27
  I18nInstance,
15
28
  DetectorCacheEntry
@@ -38,7 +51,7 @@ const DETECTOR_SAFE_OPTION_KEYS: string[] = [
38
51
  * Stable stringify that sorts object keys to ensure consistent output
39
52
  * regardless of property order
40
53
  */
41
- const stableStringify = (value: any): string => {
54
+ const stableStringify = (value: unknown): string => {
42
55
  if (value === null || value === undefined) {
43
56
  return JSON.stringify(value);
44
57
  }
@@ -53,9 +66,10 @@ const stableStringify = (value: any): string => {
53
66
  }
54
67
 
55
68
  // For objects, sort keys and recursively stringify values
56
- const sortedKeys = Object.keys(value).sort();
69
+ const record = value as Record<string, unknown>;
70
+ const sortedKeys = Object.keys(record).sort();
57
71
  const sortedEntries = sortedKeys.map(key => {
58
- const stringifiedValue = stableStringify(value[key]);
72
+ const stringifiedValue = stableStringify(record[key]);
59
73
  return `${JSON.stringify(key)}:${stringifiedValue}`;
60
74
  });
61
75
 
@@ -76,19 +90,19 @@ export const buildDetectorConfigKey = (
76
90
 
77
91
  export const pickSafeDetectionOptions = (
78
92
  userInitOptions?: I18nInitOptions,
79
- ): Partial<I18nInitOptions> & Record<string, any> => {
93
+ ): Partial<I18nInitOptions> & Record<string, unknown> => {
80
94
  if (!userInitOptions) {
81
95
  return {};
82
96
  }
83
- const safeOptions: Partial<I18nInitOptions> & Record<string, any> = {};
97
+ const safeOptions: Partial<I18nInitOptions> & Record<string, unknown> = {};
84
98
  for (const key of DETECTOR_SAFE_OPTION_KEYS) {
85
- const value = (userInitOptions as any)[key];
99
+ const value = (userInitOptions as Record<string, unknown>)[key];
86
100
  if (value !== undefined) {
87
101
  safeOptions[key] = value;
88
102
  }
89
103
  }
90
- if ((userInitOptions as any).interpolation) {
91
- safeOptions.interpolation = { ...(userInitOptions as any).interpolation };
104
+ if (userInitOptions.interpolation) {
105
+ safeOptions.interpolation = { ...userInitOptions.interpolation };
92
106
  }
93
107
  return safeOptions;
94
108
  };
@@ -97,7 +111,7 @@ const cleanupDetectorCacheEntry = (entry?: DetectorCacheEntry) => {
97
111
  if (!entry || !entry.isTemporary) {
98
112
  return;
99
113
  }
100
- const instance = entry.instance as any;
114
+ const instance = entry.instance as DetectorCleanupInstance;
101
115
  try {
102
116
  instance?.removeAllListeners?.();
103
117
  } catch (error) {