@bleedingdev/modern-js-plugin-i18n 3.5.0-ultramodern.98 → 3.8.1-ultramodern.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.
Files changed (39) hide show
  1. package/dist/cjs/runtime/context.js +1 -1
  2. package/dist/cjs/runtime/contextHelpers.js +2 -1
  3. package/dist/cjs/runtime/hooks.js +3 -0
  4. package/dist/cjs/runtime/providerComposition.js +9 -10
  5. package/dist/cjs/runtime/routerAdapter.js +23 -10
  6. package/dist/esm/runtime/context.mjs +1 -1
  7. package/dist/esm/runtime/contextHelpers.mjs +2 -1
  8. package/dist/esm/runtime/hooks.mjs +3 -0
  9. package/dist/esm/runtime/providerComposition.mjs +9 -10
  10. package/dist/esm/runtime/routerAdapter.mjs +24 -11
  11. package/dist/esm-node/runtime/context.mjs +1 -1
  12. package/dist/esm-node/runtime/contextHelpers.mjs +2 -1
  13. package/dist/esm-node/runtime/hooks.mjs +3 -0
  14. package/dist/esm-node/runtime/providerComposition.mjs +9 -10
  15. package/dist/esm-node/runtime/routerAdapter.mjs +24 -11
  16. package/dist/types/runtime/context.d.ts +9 -3
  17. package/dist/types/runtime/core.d.ts +2 -2
  18. package/dist/types/runtime/i18n/backend/middleware.common.d.ts +1 -1
  19. package/dist/types/runtime/i18n/backend/middleware.d.ts +1 -1
  20. package/dist/types/runtime/i18n/backend/middleware.node.d.ts +1 -1
  21. package/dist/types/runtime/i18n/detection/middleware.d.ts +1 -1
  22. package/dist/types/runtime/i18n/detection/middleware.node.d.ts +1 -1
  23. package/dist/types/runtime/i18n/instance.d.ts +28 -19
  24. package/package.json +19 -16
  25. package/src/runtime/context.tsx +16 -4
  26. package/src/runtime/contextHelpers.ts +10 -1
  27. package/src/runtime/core.tsx +7 -1
  28. package/src/runtime/hooks.ts +7 -0
  29. package/src/runtime/i18n/instance.ts +84 -30
  30. package/src/runtime/providerComposition.tsx +14 -13
  31. package/src/runtime/routerAdapter.tsx +81 -58
  32. package/tests/i18nUtils.test.ts +1 -0
  33. package/tests/link.test.tsx +1 -0
  34. package/tests/localisedUrls.test.ts +44 -25
  35. package/tests/reactI18nextRuntimeBoundary.test.ts +181 -31
  36. package/tests/routerAdapter.test.tsx +185 -0
  37. package/tests/type-fixture/i18nInstanceTypes.fixture.ts +29 -0
  38. package/tsconfig.json +3 -1
  39. package/tests/fixtures/localised-routes.golden.json +0 -156
@@ -154,7 +154,7 @@ const useModernI18n = ()=>{
154
154
  language: currentLanguage,
155
155
  changeLanguage,
156
156
  t,
157
- i18nInstance,
157
+ i18nInstance: i18nInstance,
158
158
  supportedLanguages: languages || [],
159
159
  localisedUrls,
160
160
  isLanguageSupported,
@@ -93,7 +93,8 @@ async function changeModernI18nLanguage(newLang, options) {
93
93
  }
94
94
  function translateI18n(i18nInstance, key, ...args) {
95
95
  if ('function' != typeof i18nInstance.t) throw new Error('i18nInstance.t required');
96
- return i18nInstance.t(key, ...args);
96
+ const translate = i18nInstance.t;
97
+ return translate.call(i18nInstance, key, ...args);
97
98
  }
98
99
  function isI18nLanguageSupported(languages, lang) {
99
100
  return languages?.includes(lang) || false;
@@ -44,6 +44,9 @@ function createMinimalI18nInstance(language) {
44
44
  language,
45
45
  isInitialized: false,
46
46
  init: ()=>Promise.resolve(void 0),
47
+ t: ()=>{
48
+ throw new Error('i18nInstance.t required');
49
+ },
47
50
  use: ()=>{},
48
51
  createInstance: ()=>minimalInstance,
49
52
  services: {}
@@ -77,21 +77,20 @@ const createI18nRootWrapper = (options)=>(App)=>(props)=>{
77
77
  forceUpdate,
78
78
  synchronizeLanguage
79
79
  ]);
80
+ const I18nextProvider = getI18nextProvider();
81
+ const i18nextInstanceForProvider = (0, external_react_namespaceObject.useMemo)(()=>i18nInstance ? (0, instance_js_namespaceObject.getI18nextInstanceForProvider)(i18nInstance, lang) : void 0, [
82
+ i18nInstance,
83
+ lang
84
+ ]);
80
85
  const children = props.children;
81
86
  let appContent = App ? /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(App, {
82
87
  ...props,
83
88
  children: children
84
89
  }) : children;
85
- if (i18nInstance) {
86
- const I18nextProvider = getI18nextProvider();
87
- if (I18nextProvider) {
88
- const i18nextInstanceForProvider = (0, instance_js_namespaceObject.getI18nextInstanceForProvider)(i18nInstance, lang);
89
- appContent = /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(I18nextProvider, {
90
- i18n: i18nextInstanceForProvider,
91
- children: appContent
92
- });
93
- }
94
- }
90
+ if (I18nextProvider && i18nextInstanceForProvider) appContent = /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(I18nextProvider, {
91
+ i18n: i18nextInstanceForProvider,
92
+ children: appContent
93
+ });
95
94
  return /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsxs)(jsx_runtime_namespaceObject.Fragment, {
96
95
  children: [
97
96
  Boolean(htmlLangAttr) && /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(head_namespaceObject.Helmet, {
@@ -32,7 +32,6 @@ __webpack_require__.d(__webpack_exports__, {
32
32
  });
33
33
  const runtime_namespaceObject = require("@modern-js/runtime");
34
34
  const context_namespaceObject = require("@modern-js/runtime/context");
35
- const router_namespaceObject = require("@modern-js/runtime/router");
36
35
  const external_react_namespaceObject = require("react");
37
36
  const normalizeUrlPart = (value, prefix)=>{
38
37
  if ('string' != typeof value || !value) return '';
@@ -82,24 +81,33 @@ const getRouterParams = (internalContext, contextRouter)=>{
82
81
  return params;
83
82
  }, {});
84
83
  };
84
+ const getRouterSnapshot = (internalContext, contextRouter)=>{
85
+ const location = getRouterStateLocation(internalContext, contextRouter);
86
+ const params = getRouterParams(internalContext, contextRouter);
87
+ return JSON.stringify([
88
+ location?.pathname ?? '',
89
+ location?.search ?? '',
90
+ location?.hash ?? '',
91
+ Object.entries(params).sort(([left], [right])=>left.localeCompare(right))
92
+ ]);
93
+ };
85
94
  const useI18nRouterAdapter = ()=>{
86
95
  const runtimeContext = (0, external_react_namespaceObject.useContext)(runtime_namespaceObject.RuntimeContext);
87
96
  const internalContext = (0, external_react_namespaceObject.useContext)(context_namespaceObject.InternalRuntimeContext);
88
- const inReactRouter = (0, router_namespaceObject.useInRouterContext)();
89
- const reactRouterNavigate = inReactRouter ? (0, router_namespaceObject.useNavigate)() : null;
90
- const reactRouterLocation = inReactRouter ? (0, router_namespaceObject.useLocation)() : null;
91
- const reactRouterParams = inReactRouter ? (0, router_namespaceObject.useParams)() : {};
97
+ const routerApi = internalContext.router || runtimeContext.router;
98
+ const inReactRouter = routerApi?.useInRouterContext?.() ?? false;
99
+ const reactRouterNavigate = inReactRouter ? routerApi?.useNavigate?.() ?? null : null;
100
+ const reactRouterLocation = inReactRouter ? routerApi?.useLocation?.() ?? null : null;
101
+ const reactRouterParams = inReactRouter ? routerApi?.useParams?.() ?? {} : {};
92
102
  const framework = getRouterFramework(runtimeContext, internalContext, inReactRouter);
93
103
  const contextUseRouter = inReactRouter || 'tanstack' !== framework ? void 0 : internalContext.router?.useRouter || runtimeContext.router?.useRouter;
94
104
  const contextRouter = contextUseRouter ? contextUseRouter({
95
105
  warn: false
96
106
  }) : null;
97
- const [, setRouterVersion] = (0, external_react_namespaceObject.useState)(0);
98
107
  const hasRouter = 'tanstack' === framework || 'react-router' === framework || Boolean(reactRouterNavigate);
99
- (0, external_react_namespaceObject.useEffect)(()=>{
108
+ const subscribeToRouter = (0, external_react_namespaceObject.useCallback)((update)=>{
100
109
  const router = getRouterInstance(internalContext, contextRouter);
101
- if (!router) return;
102
- const update = ()=>setRouterVersion((version)=>version + 1);
110
+ if (!router) return ()=>void 0;
103
111
  const unsubscribers = [];
104
112
  if ('react-router' === framework && !inReactRouter && 'function' == typeof router.subscribe) {
105
113
  const subscribe = router.subscribe;
@@ -129,6 +137,11 @@ const useI18nRouterAdapter = ()=>{
129
137
  inReactRouter,
130
138
  internalContext
131
139
  ]);
140
+ const getSnapshot = (0, external_react_namespaceObject.useCallback)(()=>getRouterSnapshot(internalContext, contextRouter), [
141
+ contextRouter,
142
+ internalContext
143
+ ]);
144
+ (0, external_react_namespaceObject.useSyncExternalStore)(subscribeToRouter, getSnapshot, getSnapshot);
132
145
  const navigate = (0, external_react_namespaceObject.useCallback)((href, options)=>{
133
146
  const router = getRouterInstance(internalContext, contextRouter);
134
147
  const activeFramework = getRouterFramework(runtimeContext, internalContext, inReactRouter);
@@ -156,7 +169,7 @@ const useI18nRouterAdapter = ()=>{
156
169
  ]);
157
170
  const location = (reactRouterLocation ? normalizeLocation(reactRouterLocation) : getRouterStateLocation(internalContext, contextRouter)) || getWindowLocation();
158
171
  const params = inReactRouter ? reactRouterParams : getRouterParams(internalContext, contextRouter);
159
- const Link = 'tanstack' === framework ? internalContext.router?.Link || runtimeContext.router?.Link || null : 'react-router' === framework || inReactRouter ? router_namespaceObject.Link : null;
172
+ const Link = 'tanstack' === framework ? internalContext.router?.Link || runtimeContext.router?.Link || null : 'react-router' === framework || inReactRouter ? routerApi?.Link ?? null : null;
160
173
  return {
161
174
  framework,
162
175
  hasRouter,
@@ -120,7 +120,7 @@ const useModernI18n = ()=>{
120
120
  language: currentLanguage,
121
121
  changeLanguage,
122
122
  t,
123
- i18nInstance,
123
+ i18nInstance: i18nInstance,
124
124
  supportedLanguages: languages || [],
125
125
  localisedUrls,
126
126
  isLanguageSupported,
@@ -55,7 +55,8 @@ async function changeModernI18nLanguage(newLang, options) {
55
55
  }
56
56
  function translateI18n(i18nInstance, key, ...args) {
57
57
  if ('function' != typeof i18nInstance.t) throw new Error('i18nInstance.t required');
58
- return i18nInstance.t(key, ...args);
58
+ const translate = i18nInstance.t;
59
+ return translate.call(i18nInstance, key, ...args);
59
60
  }
60
61
  function isI18nLanguageSupported(languages, lang) {
61
62
  return languages?.includes(lang) || false;
@@ -9,6 +9,9 @@ function createMinimalI18nInstance(language) {
9
9
  language,
10
10
  isInitialized: false,
11
11
  init: ()=>Promise.resolve(void 0),
12
+ t: ()=>{
13
+ throw new Error('i18nInstance.t required');
14
+ },
12
15
  use: ()=>{},
13
16
  createInstance: ()=>minimalInstance,
14
17
  services: {}
@@ -45,21 +45,20 @@ const createI18nRootWrapper = (options)=>(App)=>(props)=>{
45
45
  forceUpdate,
46
46
  synchronizeLanguage
47
47
  ]);
48
+ const I18nextProvider = getI18nextProvider();
49
+ const i18nextInstanceForProvider = useMemo(()=>i18nInstance ? getI18nextInstanceForProvider(i18nInstance, lang) : void 0, [
50
+ i18nInstance,
51
+ lang
52
+ ]);
48
53
  const children = props.children;
49
54
  let appContent = App ? /*#__PURE__*/ jsx(App, {
50
55
  ...props,
51
56
  children: children
52
57
  }) : children;
53
- if (i18nInstance) {
54
- const I18nextProvider = getI18nextProvider();
55
- if (I18nextProvider) {
56
- const i18nextInstanceForProvider = getI18nextInstanceForProvider(i18nInstance, lang);
57
- appContent = /*#__PURE__*/ jsx(I18nextProvider, {
58
- i18n: i18nextInstanceForProvider,
59
- children: appContent
60
- });
61
- }
62
- }
58
+ if (I18nextProvider && i18nextInstanceForProvider) appContent = /*#__PURE__*/ jsx(I18nextProvider, {
59
+ i18n: i18nextInstanceForProvider,
60
+ children: appContent
61
+ });
63
62
  return /*#__PURE__*/ jsxs(Fragment, {
64
63
  children: [
65
64
  Boolean(htmlLangAttr) && /*#__PURE__*/ jsx(Helmet, {
@@ -1,7 +1,6 @@
1
1
  import { RuntimeContext, isBrowser } from "@modern-js/runtime";
2
2
  import { InternalRuntimeContext, getRouterRuntimeState } from "@modern-js/runtime/context";
3
- import { Link as router_Link, useInRouterContext, useLocation, useNavigate, useParams } from "@modern-js/runtime/router";
4
- import { useCallback, useContext, useEffect, useState } from "react";
3
+ import { useCallback, useContext, useSyncExternalStore } from "react";
5
4
  const normalizeUrlPart = (value, prefix)=>{
6
5
  if ('string' != typeof value || !value) return '';
7
6
  return value.startsWith(prefix) ? value : `${prefix}${value}`;
@@ -50,24 +49,33 @@ const getRouterParams = (internalContext, contextRouter)=>{
50
49
  return params;
51
50
  }, {});
52
51
  };
52
+ const getRouterSnapshot = (internalContext, contextRouter)=>{
53
+ const location = getRouterStateLocation(internalContext, contextRouter);
54
+ const params = getRouterParams(internalContext, contextRouter);
55
+ return JSON.stringify([
56
+ location?.pathname ?? '',
57
+ location?.search ?? '',
58
+ location?.hash ?? '',
59
+ Object.entries(params).sort(([left], [right])=>left.localeCompare(right))
60
+ ]);
61
+ };
53
62
  const useI18nRouterAdapter = ()=>{
54
63
  const runtimeContext = useContext(RuntimeContext);
55
64
  const internalContext = useContext(InternalRuntimeContext);
56
- const inReactRouter = useInRouterContext();
57
- const reactRouterNavigate = inReactRouter ? useNavigate() : null;
58
- const reactRouterLocation = inReactRouter ? useLocation() : null;
59
- const reactRouterParams = inReactRouter ? useParams() : {};
65
+ const routerApi = internalContext.router || runtimeContext.router;
66
+ const inReactRouter = routerApi?.useInRouterContext?.() ?? false;
67
+ const reactRouterNavigate = inReactRouter ? routerApi?.useNavigate?.() ?? null : null;
68
+ const reactRouterLocation = inReactRouter ? routerApi?.useLocation?.() ?? null : null;
69
+ const reactRouterParams = inReactRouter ? routerApi?.useParams?.() ?? {} : {};
60
70
  const framework = getRouterFramework(runtimeContext, internalContext, inReactRouter);
61
71
  const contextUseRouter = inReactRouter || 'tanstack' !== framework ? void 0 : internalContext.router?.useRouter || runtimeContext.router?.useRouter;
62
72
  const contextRouter = contextUseRouter ? contextUseRouter({
63
73
  warn: false
64
74
  }) : null;
65
- const [, setRouterVersion] = useState(0);
66
75
  const hasRouter = 'tanstack' === framework || 'react-router' === framework || Boolean(reactRouterNavigate);
67
- useEffect(()=>{
76
+ const subscribeToRouter = useCallback((update)=>{
68
77
  const router = getRouterInstance(internalContext, contextRouter);
69
- if (!router) return;
70
- const update = ()=>setRouterVersion((version)=>version + 1);
78
+ if (!router) return ()=>void 0;
71
79
  const unsubscribers = [];
72
80
  if ('react-router' === framework && !inReactRouter && 'function' == typeof router.subscribe) {
73
81
  const subscribe = router.subscribe;
@@ -97,6 +105,11 @@ const useI18nRouterAdapter = ()=>{
97
105
  inReactRouter,
98
106
  internalContext
99
107
  ]);
108
+ const getSnapshot = useCallback(()=>getRouterSnapshot(internalContext, contextRouter), [
109
+ contextRouter,
110
+ internalContext
111
+ ]);
112
+ useSyncExternalStore(subscribeToRouter, getSnapshot, getSnapshot);
100
113
  const navigate = useCallback((href, options)=>{
101
114
  const router = getRouterInstance(internalContext, contextRouter);
102
115
  const activeFramework = getRouterFramework(runtimeContext, internalContext, inReactRouter);
@@ -124,7 +137,7 @@ const useI18nRouterAdapter = ()=>{
124
137
  ]);
125
138
  const location = (reactRouterLocation ? normalizeLocation(reactRouterLocation) : getRouterStateLocation(internalContext, contextRouter)) || getWindowLocation();
126
139
  const params = inReactRouter ? reactRouterParams : getRouterParams(internalContext, contextRouter);
127
- const Link = 'tanstack' === framework ? internalContext.router?.Link || runtimeContext.router?.Link || null : 'react-router' === framework || inReactRouter ? router_Link : null;
140
+ const Link = 'tanstack' === framework ? internalContext.router?.Link || runtimeContext.router?.Link || null : 'react-router' === framework || inReactRouter ? routerApi?.Link ?? null : null;
128
141
  return {
129
142
  framework,
130
143
  hasRouter,
@@ -121,7 +121,7 @@ const useModernI18n = ()=>{
121
121
  language: currentLanguage,
122
122
  changeLanguage,
123
123
  t,
124
- i18nInstance,
124
+ i18nInstance: i18nInstance,
125
125
  supportedLanguages: languages || [],
126
126
  localisedUrls,
127
127
  isLanguageSupported,
@@ -56,7 +56,8 @@ async function changeModernI18nLanguage(newLang, options) {
56
56
  }
57
57
  function translateI18n(i18nInstance, key, ...args) {
58
58
  if ('function' != typeof i18nInstance.t) throw new Error('i18nInstance.t required');
59
- return i18nInstance.t(key, ...args);
59
+ const translate = i18nInstance.t;
60
+ return translate.call(i18nInstance, key, ...args);
60
61
  }
61
62
  function isI18nLanguageSupported(languages, lang) {
62
63
  return languages?.includes(lang) || false;
@@ -10,6 +10,9 @@ function createMinimalI18nInstance(language) {
10
10
  language,
11
11
  isInitialized: false,
12
12
  init: ()=>Promise.resolve(void 0),
13
+ t: ()=>{
14
+ throw new Error('i18nInstance.t required');
15
+ },
13
16
  use: ()=>{},
14
17
  createInstance: ()=>minimalInstance,
15
18
  services: {}
@@ -46,21 +46,20 @@ const createI18nRootWrapper = (options)=>(App)=>(props)=>{
46
46
  forceUpdate,
47
47
  synchronizeLanguage
48
48
  ]);
49
+ const I18nextProvider = getI18nextProvider();
50
+ const i18nextInstanceForProvider = useMemo(()=>i18nInstance ? getI18nextInstanceForProvider(i18nInstance, lang) : void 0, [
51
+ i18nInstance,
52
+ lang
53
+ ]);
49
54
  const children = props.children;
50
55
  let appContent = App ? /*#__PURE__*/ jsx(App, {
51
56
  ...props,
52
57
  children: children
53
58
  }) : children;
54
- if (i18nInstance) {
55
- const I18nextProvider = getI18nextProvider();
56
- if (I18nextProvider) {
57
- const i18nextInstanceForProvider = getI18nextInstanceForProvider(i18nInstance, lang);
58
- appContent = /*#__PURE__*/ jsx(I18nextProvider, {
59
- i18n: i18nextInstanceForProvider,
60
- children: appContent
61
- });
62
- }
63
- }
59
+ if (I18nextProvider && i18nextInstanceForProvider) appContent = /*#__PURE__*/ jsx(I18nextProvider, {
60
+ i18n: i18nextInstanceForProvider,
61
+ children: appContent
62
+ });
64
63
  return /*#__PURE__*/ jsxs(Fragment, {
65
64
  children: [
66
65
  Boolean(htmlLangAttr) && /*#__PURE__*/ jsx(Helmet, {
@@ -1,8 +1,7 @@
1
1
  import "node:module";
2
2
  import { RuntimeContext, isBrowser } from "@modern-js/runtime";
3
3
  import { InternalRuntimeContext, getRouterRuntimeState } from "@modern-js/runtime/context";
4
- import { Link as router_Link, useInRouterContext, useLocation, useNavigate, useParams } from "@modern-js/runtime/router";
5
- import { useCallback, useContext, useEffect, useState } from "react";
4
+ import { useCallback, useContext, useSyncExternalStore } from "react";
6
5
  const normalizeUrlPart = (value, prefix)=>{
7
6
  if ('string' != typeof value || !value) return '';
8
7
  return value.startsWith(prefix) ? value : `${prefix}${value}`;
@@ -51,24 +50,33 @@ const getRouterParams = (internalContext, contextRouter)=>{
51
50
  return params;
52
51
  }, {});
53
52
  };
53
+ const getRouterSnapshot = (internalContext, contextRouter)=>{
54
+ const location = getRouterStateLocation(internalContext, contextRouter);
55
+ const params = getRouterParams(internalContext, contextRouter);
56
+ return JSON.stringify([
57
+ location?.pathname ?? '',
58
+ location?.search ?? '',
59
+ location?.hash ?? '',
60
+ Object.entries(params).sort(([left], [right])=>left.localeCompare(right))
61
+ ]);
62
+ };
54
63
  const useI18nRouterAdapter = ()=>{
55
64
  const runtimeContext = useContext(RuntimeContext);
56
65
  const internalContext = useContext(InternalRuntimeContext);
57
- const inReactRouter = useInRouterContext();
58
- const reactRouterNavigate = inReactRouter ? useNavigate() : null;
59
- const reactRouterLocation = inReactRouter ? useLocation() : null;
60
- const reactRouterParams = inReactRouter ? useParams() : {};
66
+ const routerApi = internalContext.router || runtimeContext.router;
67
+ const inReactRouter = routerApi?.useInRouterContext?.() ?? false;
68
+ const reactRouterNavigate = inReactRouter ? routerApi?.useNavigate?.() ?? null : null;
69
+ const reactRouterLocation = inReactRouter ? routerApi?.useLocation?.() ?? null : null;
70
+ const reactRouterParams = inReactRouter ? routerApi?.useParams?.() ?? {} : {};
61
71
  const framework = getRouterFramework(runtimeContext, internalContext, inReactRouter);
62
72
  const contextUseRouter = inReactRouter || 'tanstack' !== framework ? void 0 : internalContext.router?.useRouter || runtimeContext.router?.useRouter;
63
73
  const contextRouter = contextUseRouter ? contextUseRouter({
64
74
  warn: false
65
75
  }) : null;
66
- const [, setRouterVersion] = useState(0);
67
76
  const hasRouter = 'tanstack' === framework || 'react-router' === framework || Boolean(reactRouterNavigate);
68
- useEffect(()=>{
77
+ const subscribeToRouter = useCallback((update)=>{
69
78
  const router = getRouterInstance(internalContext, contextRouter);
70
- if (!router) return;
71
- const update = ()=>setRouterVersion((version)=>version + 1);
79
+ if (!router) return ()=>void 0;
72
80
  const unsubscribers = [];
73
81
  if ('react-router' === framework && !inReactRouter && 'function' == typeof router.subscribe) {
74
82
  const subscribe = router.subscribe;
@@ -98,6 +106,11 @@ const useI18nRouterAdapter = ()=>{
98
106
  inReactRouter,
99
107
  internalContext
100
108
  ]);
109
+ const getSnapshot = useCallback(()=>getRouterSnapshot(internalContext, contextRouter), [
110
+ contextRouter,
111
+ internalContext
112
+ ]);
113
+ useSyncExternalStore(subscribeToRouter, getSnapshot, getSnapshot);
101
114
  const navigate = useCallback((href, options)=>{
102
115
  const router = getRouterInstance(internalContext, contextRouter);
103
116
  const activeFramework = getRouterFramework(runtimeContext, internalContext, inReactRouter);
@@ -125,7 +138,7 @@ const useI18nRouterAdapter = ()=>{
125
138
  ]);
126
139
  const location = (reactRouterLocation ? normalizeLocation(reactRouterLocation) : getRouterStateLocation(internalContext, contextRouter)) || getWindowLocation();
127
140
  const params = inReactRouter ? reactRouterParams : getRouterParams(internalContext, contextRouter);
128
- const Link = 'tanstack' === framework ? internalContext.router?.Link || runtimeContext.router?.Link || null : 'react-router' === framework || inReactRouter ? router_Link : null;
141
+ const Link = 'tanstack' === framework ? internalContext.router?.Link || runtimeContext.router?.Link || null : 'react-router' === framework || inReactRouter ? routerApi?.Link ?? null : null;
129
142
  return {
130
143
  framework,
131
144
  hasRouter,
@@ -31,11 +31,11 @@ export interface FederatedI18nBoundaryProps {
31
31
  * resources used below this boundary.
32
32
  */
33
33
  export declare const FederatedI18nBoundary: FC<FederatedI18nBoundaryProps>;
34
- interface UseModernI18nReturn {
34
+ export interface UseModernI18nReturn<TInstance extends I18nInstance = I18nInstance> {
35
35
  language: string;
36
36
  changeLanguage: (newLang: string) => Promise<void>;
37
37
  t: (key: string | string[], ...args: any[]) => string;
38
- i18nInstance: I18nInstance;
38
+ i18nInstance: TInstance;
39
39
  supportedLanguages: string[];
40
40
  localisedUrls?: LocalisedUrlsOption;
41
41
  isLanguageSupported: (lang: string) => boolean;
@@ -51,8 +51,14 @@ interface UseModernI18nReturn {
51
51
  * - List of supported languages
52
52
  * - Helper function to check if language is supported
53
53
  *
54
+ * @typeParam TInstance - The concrete shape of the i18n instance held by the
55
+ * provider (e.g. i18next's `i18n`, or a wrapper type). Constrained to
56
+ * `I18nInstance`, so a nonsense argument is rejected; within that constraint it
57
+ * is still a caller assertion — the provider stores the base type and the
58
+ * narrowing is not verified at runtime. Pass it only when you know which
59
+ * instance the provider was given.
54
60
  * @param options - Optional configuration to override context settings
55
61
  * @returns Object containing i18n functionality and utilities
56
62
  */
57
- export declare const useModernI18n: () => UseModernI18nReturn;
63
+ export declare const useModernI18n: <TInstance extends I18nInstance = I18nInstance>() => UseModernI18nReturn<TInstance>;
58
64
  export {};
@@ -4,7 +4,7 @@ import type { I18nInitOptions, I18nInstance } from './i18n';
4
4
  import { type LoadReactI18nextIntegration } from './reactI18next';
5
5
  import './types';
6
6
  export type { I18nSdkLoader, I18nSdkLoadOptions } from '../shared/type';
7
- export type { Resources } from './i18n/instance';
7
+ export type { I18nInitOptions, I18nInstance, Resources, TranslateFn, } from './i18n/instance';
8
8
  export type { LoadReactI18nextIntegration, ReactI18nextIntegration, } from './reactI18next';
9
9
  export interface I18nPluginOptions {
10
10
  entryName?: string;
@@ -19,7 +19,7 @@ export interface I18nPluginOptions {
19
19
  }
20
20
  export declare const createI18nPlugin: (loadReactI18nextIntegration?: LoadReactI18nextIntegration) => ((options: I18nPluginOptions) => RuntimePlugin);
21
21
  export type { AllowedLinkTarget, CanonicalRoutePath, UltramodernCanonicalRoutes, } from './canonicalRoutes';
22
- export { FederatedI18nBoundary, type FederatedI18nBoundaryProps, useModernI18n, } from './context';
22
+ export { FederatedI18nBoundary, type FederatedI18nBoundaryProps, type UseModernI18nReturn, useModernI18n, } from './context';
23
23
  export { I18nLink, type I18nLinkProps } from './I18nLink';
24
24
  export { Link, type LinkActiveOptions, type LinkBaseProps, type LinkParams, type LinkProps, } from './Link';
25
25
  export { canonicalPath, type LocalizedPathsConfig, localizePath, type UseLocalizedLocationReturn, type UseLocalizedPathsReturn, useLocalizedLocation, useLocalizedPaths, } from './localizedPaths';
@@ -11,5 +11,5 @@ type BackendConstructor = new (...args: never[]) => unknown;
11
11
  * @param BackendBase - The base backend class (for non-chained use)
12
12
  * @param backend - Optional backend configuration
13
13
  */
14
- export declare function useI18nextBackendCommon(i18nInstance: I18nInstance, BackendWithSave: BackendConstructor, BackendBase: BackendConstructor, backend?: BackendConfigWithChained): void;
14
+ export declare function useI18nextBackendCommon(i18nInstance: I18nInstance, BackendWithSave: BackendConstructor, BackendBase: BackendConstructor, backend?: BackendConfigWithChained): unknown;
15
15
  export {};
@@ -9,4 +9,4 @@ import type { I18nInstance } from '../instance';
9
9
  export declare class HttpBackendWithSave extends Backend {
10
10
  save(_language: string, _namespace: string, _data: unknown): void;
11
11
  }
12
- export declare const useI18nextBackend: (i18nInstance: I18nInstance, backend?: ExtendedBackendOptions) => void;
12
+ export declare const useI18nextBackend: (i18nInstance: I18nInstance, backend?: ExtendedBackendOptions) => unknown;
@@ -12,5 +12,5 @@ export declare class FsBackendWithSave extends Backend {
12
12
  save(_language: string, _namespace: string, _data: unknown): void;
13
13
  }
14
14
  export declare const HttpBackendWithSave: typeof FsBackendWithSave;
15
- export declare const useI18nextBackend: (i18nInstance: I18nInstance, backend?: ExtendedBackendOptions) => void;
15
+ export declare const useI18nextBackend: (i18nInstance: I18nInstance, backend?: ExtendedBackendOptions) => unknown;
16
16
  export {};
@@ -4,7 +4,7 @@ import type { I18nInstance, LanguageDetectorOptions } from '../instance';
4
4
  * Must be called before init() to properly register the detector
5
5
  * For wrapper instances, ensure detector is registered on the underlying i18next instance
6
6
  */
7
- export declare const useI18nextLanguageDetector: (i18nInstance: I18nInstance) => void | I18nInstance;
7
+ export declare const useI18nextLanguageDetector: (i18nInstance: I18nInstance) => unknown;
8
8
  /**
9
9
  * Read language directly from localStorage/cookie
10
10
  * Fallback when detector is not available in services
@@ -9,7 +9,7 @@ export declare const readLanguageFromStorage: (_detectionOptions?: LanguageDetec
9
9
  * Register LanguageDetector plugin to i18n instance
10
10
  * Must be called before init() to properly register the detector
11
11
  */
12
- export declare const useI18nextLanguageDetector: (i18nInstance: I18nInstance) => void | I18nInstance;
12
+ export declare const useI18nextLanguageDetector: (i18nInstance: I18nInstance) => unknown;
13
13
  /**
14
14
  * Detect language using i18next-http-middleware LanguageDetector
15
15
  * For initialized instances without detector in services, manually create a detector instance
@@ -14,36 +14,45 @@ type I18nWrapperInstance = I18nInstance & {
14
14
  };
15
15
  export declare function isI18nWrapperInstance(obj: unknown): obj is I18nWrapperInstance;
16
16
  export declare function getActualI18nextInstance(instance: I18nInstance): I18nInstance;
17
+ export type TranslateFn = (key: string | string[], options?: Record<string, unknown>) => string;
17
18
  export interface I18nInstance {
18
19
  language: string;
19
20
  isInitialized?: boolean;
20
- init: {
21
- (callback?: (error: unknown, t: unknown) => void): Promise<unknown>;
22
- (options: I18nInitOptions, callback?: (error: unknown, t: unknown) => void): Promise<unknown>;
23
- };
24
- changeLanguage?: (lng?: string, callback?: (error: unknown, t: unknown) => void) => Promise<unknown>;
21
+ init(options?: any, callback?: any): Promise<any>;
22
+ changeLanguage?(lng?: string, callback?: any): Promise<any>;
25
23
  setLang?: (lang: string) => void | Promise<void>;
26
- use: (plugin: unknown) => void;
27
- createInstance?: (options?: I18nInitOptions) => I18nInstance;
28
- cloneInstance?: (options?: I18nInitOptions) => I18nInstance;
24
+ use(plugin: any): unknown;
25
+ t: TranslateFn;
26
+ exists?: (key: string | string[], options?: Record<string, unknown>) => boolean;
27
+ getFixedT?: (lng: string | readonly string[] | null, ns?: string | readonly string[] | null, keyPrefix?: string) => TranslateFn;
28
+ hasLoadedNamespace?: (ns: string | readonly string[], options?: Record<string, unknown>) => boolean;
29
+ dir?: (lng?: string) => string;
30
+ format?: (value: unknown, format?: string, lng?: string, options?: Record<string, unknown>) => string;
31
+ languages?: readonly string[];
32
+ resolvedLanguage?: string;
33
+ loadNamespaces?: (ns: string | readonly string[], callback?: (...args: any[]) => void) => Promise<void>;
34
+ loadLanguages?: (lngs: string | readonly string[], callback?: (...args: any[]) => void) => Promise<void>;
35
+ addResourceBundle?: (lng: string, ns: string, resources: Record<string, unknown>, deep?: boolean, overwrite?: boolean) => unknown;
36
+ getResourceBundle?: (lng: string, ns: string) => unknown;
37
+ getDataByLanguage?: (lng: string) => Record<string, Record<string, string>> | undefined;
38
+ createInstance?(options?: any, callback?: any): I18nInstance;
39
+ cloneInstance?(options?: any, callback?: any): I18nInstance;
29
40
  store?: I18nResourceStore;
30
- emit?: (event: string, ...args: unknown[]) => void;
31
- reloadResources?: (language?: string, namespace?: string) => Promise<void>;
32
- removeResourceBundle?: (language: string, namespace: string) => I18nInstance;
41
+ emit?(event: string, ...args: any[]): unknown;
42
+ reloadResources?(language?: any, namespace?: any, callback?: any): Promise<void>;
43
+ removeResourceBundle?(language: string, namespace: string): I18nInstance;
33
44
  services?: {
34
- languageDetector?: {
35
- detect: (request?: unknown, options?: unknown) => string | string[] | undefined;
36
- [key: string]: unknown;
37
- };
45
+ store?: unknown;
46
+ languageDetector?: any;
38
47
  resourceStore?: I18nResourceStore;
39
48
  backend?: unknown;
40
- [key: string]: unknown;
41
49
  };
42
50
  options?: {
43
- backend?: BackendOptions;
44
- [key: string]: unknown;
51
+ detection?: any;
52
+ backend?: any;
53
+ ns?: any;
54
+ defaultNS?: any;
45
55
  };
46
- [key: string]: unknown;
47
56
  }
48
57
  type LanguageDetectorOrder = string[];
49
58
  type LanguageDetectorCaches = boolean | string[];