@bleedingdev/modern-js-i18n-integration 0.0.0 → 3.9.0-ultramodern.10

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 (58) hide show
  1. package/LICENSE +21 -0
  2. package/dist/cjs/cli.js +71 -0
  3. package/dist/cjs/federation/boundary.js +100 -0
  4. package/dist/cjs/federation/registration.js +43 -0
  5. package/dist/cjs/languageSynchronization.js +44 -0
  6. package/dist/cjs/navigation.js +43 -0
  7. package/dist/cjs/options.js +18 -0
  8. package/dist/cjs/runtime-no-react-i18next.js +46 -0
  9. package/dist/cjs/runtime.js +46 -0
  10. package/dist/cjs/runtimeComposition.js +60 -0
  11. package/dist/cjs/server.js +50 -0
  12. package/dist/cjs/urlStrategy.js +51 -0
  13. package/dist/esm/cli.mjs +31 -0
  14. package/dist/esm/federation/boundary.mjs +62 -0
  15. package/dist/esm/federation/registration.mjs +5 -0
  16. package/dist/esm/languageSynchronization.mjs +6 -0
  17. package/dist/esm/navigation.mjs +1 -0
  18. package/dist/esm/options.mjs +0 -0
  19. package/dist/esm/runtime-no-react-i18next.mjs +6 -0
  20. package/dist/esm/runtime.mjs +6 -0
  21. package/dist/esm/runtimeComposition.mjs +22 -0
  22. package/dist/esm/server.mjs +10 -0
  23. package/dist/esm/urlStrategy.mjs +13 -0
  24. package/dist/esm-node/cli.mjs +32 -0
  25. package/dist/esm-node/federation/boundary.mjs +63 -0
  26. package/dist/esm-node/federation/registration.mjs +6 -0
  27. package/dist/esm-node/languageSynchronization.mjs +7 -0
  28. package/dist/esm-node/navigation.mjs +2 -0
  29. package/dist/esm-node/options.mjs +1 -0
  30. package/dist/esm-node/runtime-no-react-i18next.mjs +7 -0
  31. package/dist/esm-node/runtime.mjs +7 -0
  32. package/dist/esm-node/runtimeComposition.mjs +23 -0
  33. package/dist/esm-node/server.mjs +11 -0
  34. package/dist/esm-node/urlStrategy.mjs +14 -0
  35. package/dist/types/cli.d.ts +4 -0
  36. package/dist/types/federation/boundary.d.ts +9 -0
  37. package/dist/types/federation/registration.d.ts +2 -0
  38. package/dist/types/languageSynchronization.d.ts +3 -0
  39. package/dist/types/navigation.d.ts +9 -0
  40. package/dist/types/options.d.ts +13 -0
  41. package/dist/types/runtime-no-react-i18next.d.ts +2 -0
  42. package/dist/types/runtime.d.ts +2 -0
  43. package/dist/types/runtimeComposition.d.ts +4 -0
  44. package/dist/types/server.d.ts +3 -0
  45. package/dist/types/urlStrategy.d.ts +4 -0
  46. package/package.json +139 -5
  47. package/src/cli.ts +38 -0
  48. package/src/federation/boundary.tsx +129 -0
  49. package/src/federation/registration.ts +8 -0
  50. package/src/languageSynchronization.tsx +23 -0
  51. package/src/navigation.tsx +12 -0
  52. package/src/options.ts +18 -0
  53. package/src/runtime-no-react-i18next.ts +5 -0
  54. package/src/runtime.ts +5 -0
  55. package/src/runtimeComposition.ts +33 -0
  56. package/src/server.ts +27 -0
  57. package/src/urlStrategy.ts +19 -0
  58. package/README.md +0 -3
@@ -0,0 +1,62 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { ModernI18nContext, ModernI18nProvider, ReactI18nextProviderContext, getActualI18nextInstance } from "@modern-js/plugin-i18n/runtime/consumer";
3
+ import { useContext, useMemo } from "react";
4
+ const FederatedI18nBoundary = ({ children, defaultNamespace, fallbackLanguage, resources, supportedLanguages })=>{
5
+ const parent = useContext(ModernI18nContext);
6
+ const I18nextProvider = useContext(ReactI18nextProviderContext);
7
+ if (!parent) throw new Error('FederatedI18nBoundary must be used within ModernI18nProvider');
8
+ const languages = supportedLanguages ?? parent.languages ?? Object.keys(resources);
9
+ const scopedInstance = useMemo(()=>{
10
+ const parentInstance = getActualI18nextInstance(parent.i18nInstance);
11
+ const clone = parentInstance.cloneInstance?.({
12
+ defaultNS: defaultNamespace,
13
+ fallbackLng: fallbackLanguage ?? languages[0] ?? parent.language,
14
+ forkResourceStore: true,
15
+ initImmediate: false,
16
+ lng: parent.language,
17
+ ns: [
18
+ defaultNamespace
19
+ ],
20
+ resources,
21
+ supportedLngs: languages
22
+ });
23
+ if (!clone) throw new Error('FederatedI18nBoundary requires an i18n instance with cloneInstance support');
24
+ const resourceStore = clone.store;
25
+ const parentResourceStore = parentInstance.store;
26
+ const sharesNestedResourceState = Object.entries(resourceStore?.data ?? {}).some(([language, namespaces])=>{
27
+ const parentNamespaces = parentResourceStore?.data?.[language];
28
+ return namespaces === parentNamespaces || Object.entries(namespaces).some(([namespace, resource])=>null !== resource && 'object' == typeof resource && resource === parentNamespaces?.[namespace]);
29
+ });
30
+ if (clone === parentInstance || resourceStore === parentResourceStore || resourceStore?.data === parentResourceStore?.data || sharesNestedResourceState) throw new Error('FederatedI18nBoundary cloneInstance did not isolate the host resource store');
31
+ if (!resourceStore?.addResourceBundle || !clone.removeResourceBundle || !resourceStore.data) throw new Error('FederatedI18nBoundary requires an isolated mutable i18n resource store');
32
+ for (const [language, namespaces] of Object.entries(resourceStore.data))for (const namespace of Object.keys(namespaces))clone.removeResourceBundle(language, namespace);
33
+ for (const [language, namespaces] of Object.entries(resources))for (const [namespace, resource] of Object.entries(namespaces))resourceStore.addResourceBundle(language, namespace, resource, true, true);
34
+ return clone;
35
+ }, [
36
+ defaultNamespace,
37
+ fallbackLanguage,
38
+ languages,
39
+ parent.i18nInstance,
40
+ parent.language,
41
+ resources
42
+ ]);
43
+ const value = useMemo(()=>({
44
+ ...parent,
45
+ i18nInstance: scopedInstance,
46
+ language: parent.language,
47
+ languages
48
+ }), [
49
+ languages,
50
+ parent,
51
+ scopedInstance
52
+ ]);
53
+ const scopedContent = /*#__PURE__*/ jsx(ModernI18nProvider, {
54
+ value: value,
55
+ children: children
56
+ });
57
+ return I18nextProvider ? /*#__PURE__*/ jsx(I18nextProvider, {
58
+ i18n: scopedInstance,
59
+ children: scopedContent
60
+ }) : scopedContent;
61
+ };
62
+ export { FederatedI18nBoundary };
@@ -0,0 +1,5 @@
1
+ import { FederatedI18nBoundary } from "./boundary.mjs";
2
+ function registerFederatedI18nBoundary(api) {
3
+ api.resolveComponent((component, { name })=>'i18n.FederatedI18nBoundary' === name ? FederatedI18nBoundary : component);
4
+ }
5
+ export { registerFederatedI18nBoundary };
@@ -0,0 +1,6 @@
1
+ import { useLanguageSync } from "@modern-js/i18n-runtime-extensions";
2
+ const I18nLanguageSynchronization = ({ i18nInstance, localePathRedirect, languages, pathname, prevLangRef, setLang, children })=>{
3
+ const synchronizeLanguage = useLanguageSync(i18nInstance, localePathRedirect, languages, pathname, prevLangRef, setLang);
4
+ return children(synchronizeLanguage);
5
+ };
6
+ export { I18nLanguageSynchronization };
@@ -0,0 +1 @@
1
+ export { I18nRouterNavigationProvider, useIntegratedRouterAdapter } from "@modern-js/plugin-i18n/runtime/no-react-i18next";
File without changes
@@ -0,0 +1,6 @@
1
+ import { i18nPlugin } from "@modern-js/plugin-i18n/runtime/no-react-i18next";
2
+ import { composeI18nPlugin } from "./runtimeComposition.mjs";
3
+ const runtime_no_react_i18next_i18nPlugin = composeI18nPlugin(i18nPlugin);
4
+ const runtime_no_react_i18next = runtime_no_react_i18next_i18nPlugin;
5
+ export default runtime_no_react_i18next;
6
+ export { runtime_no_react_i18next_i18nPlugin as i18nPlugin };
@@ -0,0 +1,6 @@
1
+ import { i18nPlugin } from "@modern-js/plugin-i18n/runtime";
2
+ import { composeI18nPlugin } from "./runtimeComposition.mjs";
3
+ const runtime_i18nPlugin = composeI18nPlugin(i18nPlugin);
4
+ const runtime = runtime_i18nPlugin;
5
+ export default runtime;
6
+ export { runtime_i18nPlugin as i18nPlugin };
@@ -0,0 +1,22 @@
1
+ import { registerFederatedI18nBoundary } from "./federation/registration.mjs";
2
+ import { I18nLanguageSynchronization } from "./languageSynchronization.mjs";
3
+ import { I18nRouterNavigationProvider } from "./navigation.mjs";
4
+ import { createI18nUrlStrategy } from "./urlStrategy.mjs";
5
+ const composeI18nPlugin = (nativeI18nPlugin)=>(options = {})=>{
6
+ const { localisedUrls, localeDetectionByEntry: _entryOptions, ...nativeLocaleDetection } = options.localeDetection ?? {};
7
+ const native = nativeI18nPlugin({
8
+ ...options,
9
+ localeDetection: nativeLocaleDetection,
10
+ urlStrategy: options.urlStrategy ?? createI18nUrlStrategy(localisedUrls),
11
+ NavigationProvider: options.NavigationProvider ?? I18nRouterNavigationProvider,
12
+ LanguageSynchronization: options.LanguageSynchronization ?? I18nLanguageSynchronization
13
+ });
14
+ return {
15
+ ...native,
16
+ setup (api) {
17
+ registerFederatedI18nBoundary(api);
18
+ return native.setup?.(api);
19
+ }
20
+ };
21
+ };
22
+ export { composeI18nPlugin };
@@ -0,0 +1,10 @@
1
+ import { getLocaleDetectionOptions } from "@modern-js/plugin-i18n/config";
2
+ import { i18nServerPlugin } from "@modern-js/plugin-i18n/server";
3
+ import { createI18nUrlStrategy } from "./urlStrategy.mjs";
4
+ const server_i18nServerPlugin = (options)=>i18nServerPlugin({
5
+ ...options,
6
+ resolveUrlStrategy: options.resolveUrlStrategy ?? ((entryName)=>createI18nUrlStrategy(options.localeDetection ? getLocaleDetectionOptions(entryName, options.localeDetection).localisedUrls : void 0))
7
+ });
8
+ const server = server_i18nServerPlugin;
9
+ export default server;
10
+ export { server_i18nServerPlugin as i18nServerPlugin };
@@ -0,0 +1,13 @@
1
+ import { canonicalTargetPathname, isDefaultLocaleRedirectSkipPath, localiseTargetPathname } from "@modern-js/i18n-runtime-extensions";
2
+ const createI18nUrlStrategy = (localisedUrls)=>({
3
+ localizePathname: (pathname, language, languages)=>localiseTargetPathname(pathname, language, [
4
+ ...languages
5
+ ], localisedUrls),
6
+ canonicalPathname: (pathname, languages)=>canonicalTargetPathname(pathname, [
7
+ ...languages
8
+ ], localisedUrls),
9
+ shouldSkipRedirect: (pathname, languages)=>isDefaultLocaleRedirectSkipPath(pathname, [
10
+ ...languages
11
+ ])
12
+ });
13
+ export { createI18nUrlStrategy };
@@ -0,0 +1,32 @@
1
+ import "node:module";
2
+ const runtimePaths = new Map([
3
+ [
4
+ '@modern-js/plugin-i18n/runtime',
5
+ '@modern-js/i18n-integration/runtime'
6
+ ],
7
+ [
8
+ '@modern-js/plugin-i18n/runtime/no-react-i18next',
9
+ '@modern-js/i18n-integration/runtime/no-react-i18next'
10
+ ]
11
+ ]);
12
+ const ultramodernI18nIntegrationPlugin = ()=>({
13
+ name: '@modern-js/i18n-integration',
14
+ pre: [
15
+ '@modern-js/plugin-i18n'
16
+ ],
17
+ setup (api) {
18
+ api._internalRuntimePlugins(({ entrypoint, plugins })=>({
19
+ entrypoint,
20
+ plugins: plugins.map((plugin)=>{
21
+ const path = runtimePaths.get(plugin.path);
22
+ return path ? {
23
+ ...plugin,
24
+ path
25
+ } : plugin;
26
+ })
27
+ }));
28
+ }
29
+ });
30
+ const cli = ultramodernI18nIntegrationPlugin;
31
+ export default cli;
32
+ export { ultramodernI18nIntegrationPlugin };
@@ -0,0 +1,63 @@
1
+ import "node:module";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import { ModernI18nContext, ModernI18nProvider, ReactI18nextProviderContext, getActualI18nextInstance } from "@modern-js/plugin-i18n/runtime/consumer";
4
+ import { useContext, useMemo } from "react";
5
+ const FederatedI18nBoundary = ({ children, defaultNamespace, fallbackLanguage, resources, supportedLanguages })=>{
6
+ const parent = useContext(ModernI18nContext);
7
+ const I18nextProvider = useContext(ReactI18nextProviderContext);
8
+ if (!parent) throw new Error('FederatedI18nBoundary must be used within ModernI18nProvider');
9
+ const languages = supportedLanguages ?? parent.languages ?? Object.keys(resources);
10
+ const scopedInstance = useMemo(()=>{
11
+ const parentInstance = getActualI18nextInstance(parent.i18nInstance);
12
+ const clone = parentInstance.cloneInstance?.({
13
+ defaultNS: defaultNamespace,
14
+ fallbackLng: fallbackLanguage ?? languages[0] ?? parent.language,
15
+ forkResourceStore: true,
16
+ initImmediate: false,
17
+ lng: parent.language,
18
+ ns: [
19
+ defaultNamespace
20
+ ],
21
+ resources,
22
+ supportedLngs: languages
23
+ });
24
+ if (!clone) throw new Error('FederatedI18nBoundary requires an i18n instance with cloneInstance support');
25
+ const resourceStore = clone.store;
26
+ const parentResourceStore = parentInstance.store;
27
+ const sharesNestedResourceState = Object.entries(resourceStore?.data ?? {}).some(([language, namespaces])=>{
28
+ const parentNamespaces = parentResourceStore?.data?.[language];
29
+ return namespaces === parentNamespaces || Object.entries(namespaces).some(([namespace, resource])=>null !== resource && 'object' == typeof resource && resource === parentNamespaces?.[namespace]);
30
+ });
31
+ if (clone === parentInstance || resourceStore === parentResourceStore || resourceStore?.data === parentResourceStore?.data || sharesNestedResourceState) throw new Error('FederatedI18nBoundary cloneInstance did not isolate the host resource store');
32
+ if (!resourceStore?.addResourceBundle || !clone.removeResourceBundle || !resourceStore.data) throw new Error('FederatedI18nBoundary requires an isolated mutable i18n resource store');
33
+ for (const [language, namespaces] of Object.entries(resourceStore.data))for (const namespace of Object.keys(namespaces))clone.removeResourceBundle(language, namespace);
34
+ for (const [language, namespaces] of Object.entries(resources))for (const [namespace, resource] of Object.entries(namespaces))resourceStore.addResourceBundle(language, namespace, resource, true, true);
35
+ return clone;
36
+ }, [
37
+ defaultNamespace,
38
+ fallbackLanguage,
39
+ languages,
40
+ parent.i18nInstance,
41
+ parent.language,
42
+ resources
43
+ ]);
44
+ const value = useMemo(()=>({
45
+ ...parent,
46
+ i18nInstance: scopedInstance,
47
+ language: parent.language,
48
+ languages
49
+ }), [
50
+ languages,
51
+ parent,
52
+ scopedInstance
53
+ ]);
54
+ const scopedContent = /*#__PURE__*/ jsx(ModernI18nProvider, {
55
+ value: value,
56
+ children: children
57
+ });
58
+ return I18nextProvider ? /*#__PURE__*/ jsx(I18nextProvider, {
59
+ i18n: scopedInstance,
60
+ children: scopedContent
61
+ }) : scopedContent;
62
+ };
63
+ export { FederatedI18nBoundary };
@@ -0,0 +1,6 @@
1
+ import "node:module";
2
+ import { FederatedI18nBoundary } from "./boundary.mjs";
3
+ function registerFederatedI18nBoundary(api) {
4
+ api.resolveComponent((component, { name })=>'i18n.FederatedI18nBoundary' === name ? FederatedI18nBoundary : component);
5
+ }
6
+ export { registerFederatedI18nBoundary };
@@ -0,0 +1,7 @@
1
+ import "node:module";
2
+ import { useLanguageSync } from "@modern-js/i18n-runtime-extensions";
3
+ const I18nLanguageSynchronization = ({ i18nInstance, localePathRedirect, languages, pathname, prevLangRef, setLang, children })=>{
4
+ const synchronizeLanguage = useLanguageSync(i18nInstance, localePathRedirect, languages, pathname, prevLangRef, setLang);
5
+ return children(synchronizeLanguage);
6
+ };
7
+ export { I18nLanguageSynchronization };
@@ -0,0 +1,2 @@
1
+ import "node:module";
2
+ export { I18nRouterNavigationProvider, useIntegratedRouterAdapter } from "@modern-js/plugin-i18n/runtime/no-react-i18next";
@@ -0,0 +1 @@
1
+ import "node:module";
@@ -0,0 +1,7 @@
1
+ import "node:module";
2
+ import { i18nPlugin } from "@modern-js/plugin-i18n/runtime/no-react-i18next";
3
+ import { composeI18nPlugin } from "./runtimeComposition.mjs";
4
+ const runtime_no_react_i18next_i18nPlugin = composeI18nPlugin(i18nPlugin);
5
+ const runtime_no_react_i18next = runtime_no_react_i18next_i18nPlugin;
6
+ export default runtime_no_react_i18next;
7
+ export { runtime_no_react_i18next_i18nPlugin as i18nPlugin };
@@ -0,0 +1,7 @@
1
+ import "node:module";
2
+ import { i18nPlugin } from "@modern-js/plugin-i18n/runtime";
3
+ import { composeI18nPlugin } from "./runtimeComposition.mjs";
4
+ const runtime_i18nPlugin = composeI18nPlugin(i18nPlugin);
5
+ const runtime = runtime_i18nPlugin;
6
+ export default runtime;
7
+ export { runtime_i18nPlugin as i18nPlugin };
@@ -0,0 +1,23 @@
1
+ import "node:module";
2
+ import { registerFederatedI18nBoundary } from "./federation/registration.mjs";
3
+ import { I18nLanguageSynchronization } from "./languageSynchronization.mjs";
4
+ import { I18nRouterNavigationProvider } from "./navigation.mjs";
5
+ import { createI18nUrlStrategy } from "./urlStrategy.mjs";
6
+ const composeI18nPlugin = (nativeI18nPlugin)=>(options = {})=>{
7
+ const { localisedUrls, localeDetectionByEntry: _entryOptions, ...nativeLocaleDetection } = options.localeDetection ?? {};
8
+ const native = nativeI18nPlugin({
9
+ ...options,
10
+ localeDetection: nativeLocaleDetection,
11
+ urlStrategy: options.urlStrategy ?? createI18nUrlStrategy(localisedUrls),
12
+ NavigationProvider: options.NavigationProvider ?? I18nRouterNavigationProvider,
13
+ LanguageSynchronization: options.LanguageSynchronization ?? I18nLanguageSynchronization
14
+ });
15
+ return {
16
+ ...native,
17
+ setup (api) {
18
+ registerFederatedI18nBoundary(api);
19
+ return native.setup?.(api);
20
+ }
21
+ };
22
+ };
23
+ export { composeI18nPlugin };
@@ -0,0 +1,11 @@
1
+ import "node:module";
2
+ import { getLocaleDetectionOptions } from "@modern-js/plugin-i18n/config";
3
+ import { i18nServerPlugin } from "@modern-js/plugin-i18n/server";
4
+ import { createI18nUrlStrategy } from "./urlStrategy.mjs";
5
+ const server_i18nServerPlugin = (options)=>i18nServerPlugin({
6
+ ...options,
7
+ resolveUrlStrategy: options.resolveUrlStrategy ?? ((entryName)=>createI18nUrlStrategy(options.localeDetection ? getLocaleDetectionOptions(entryName, options.localeDetection).localisedUrls : void 0))
8
+ });
9
+ const server = server_i18nServerPlugin;
10
+ export default server;
11
+ export { server_i18nServerPlugin as i18nServerPlugin };
@@ -0,0 +1,14 @@
1
+ import "node:module";
2
+ import { canonicalTargetPathname, isDefaultLocaleRedirectSkipPath, localiseTargetPathname } from "@modern-js/i18n-runtime-extensions";
3
+ const createI18nUrlStrategy = (localisedUrls)=>({
4
+ localizePathname: (pathname, language, languages)=>localiseTargetPathname(pathname, language, [
5
+ ...languages
6
+ ], localisedUrls),
7
+ canonicalPathname: (pathname, languages)=>canonicalTargetPathname(pathname, [
8
+ ...languages
9
+ ], localisedUrls),
10
+ shouldSkipRedirect: (pathname, languages)=>isDefaultLocaleRedirectSkipPath(pathname, [
11
+ ...languages
12
+ ])
13
+ });
14
+ export { createI18nUrlStrategy };
@@ -0,0 +1,4 @@
1
+ import type { AppTools, CliPlugin } from '@modern-js/app-tools';
2
+ /** Compose fork policy with the native plugin's own descriptor generation. */
3
+ export declare const ultramodernI18nIntegrationPlugin: () => CliPlugin<AppTools>;
4
+ export default ultramodernI18nIntegrationPlugin;
@@ -0,0 +1,9 @@
1
+ import { type FederatedI18nBoundaryProps } from '@modern-js/plugin-i18n/runtime/consumer';
2
+ import { type FC } from 'react';
3
+ export type { FederatedI18nBoundaryProps } from '@modern-js/plugin-i18n/runtime/consumer';
4
+ /**
5
+ * Keeps a federated surface's translation resources inside its delivery unit.
6
+ * The host supplies only the active language; the remote owns and versions the
7
+ * resources used below this boundary.
8
+ */
9
+ export declare const FederatedI18nBoundary: FC<FederatedI18nBoundaryProps>;
@@ -0,0 +1,2 @@
1
+ import type { RuntimePluginAPI } from '@modern-js/plugin/runtime';
2
+ export declare function registerFederatedI18nBoundary(api: RuntimePluginAPI<{}>): void;
@@ -0,0 +1,3 @@
1
+ import type { I18nLanguageSynchronizationProps } from '@modern-js/plugin-i18n/runtime/no-react-i18next';
2
+ /** Bind the shared controller to this native provider's instance and state. */
3
+ export declare const I18nLanguageSynchronization: ({ i18nInstance, localePathRedirect, languages, pathname, prevLangRef, setLang, children, }: I18nLanguageSynchronizationProps) => import("react").ReactNode;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * The selected-router navigation adapter now lives in
3
+ * `@modern-js/plugin-i18n`, so a bare `appTools() + i18nPlugin()` app gets
4
+ * mapped `<Link>` navigation and language-invariant active state without this
5
+ * integration package. Re-exported here so the composed
6
+ * `ultramodernI18nIntegrationPlugin` keeps its own entry point and any consumer
7
+ * importing it from this package continues to work.
8
+ */
9
+ export { I18nRouterNavigationProvider, useIntegratedRouterAdapter, } from '@modern-js/plugin-i18n/runtime/no-react-i18next';
@@ -0,0 +1,13 @@
1
+ import type { LocalisedUrlsOption } from '@modern-js/i18n-runtime-extensions';
2
+ import type { I18nPluginOptions as NativeRuntimeOptions } from '@modern-js/plugin-i18n/runtime/no-react-i18next';
3
+ import type { I18nPluginOptions as NativeServerOptions } from '@modern-js/plugin-i18n/server';
4
+ export type IntegratedLocaleDetectionOptions = NonNullable<NativeRuntimeOptions['localeDetection']> & {
5
+ localisedUrls?: LocalisedUrlsOption;
6
+ localeDetectionByEntry?: Record<string, IntegratedLocaleDetectionOptions>;
7
+ };
8
+ export type I18nPluginOptions = NativeRuntimeOptions & {
9
+ localeDetection?: IntegratedLocaleDetectionOptions;
10
+ };
11
+ export type I18nServerPluginOptions = NativeServerOptions & {
12
+ localeDetection: IntegratedLocaleDetectionOptions;
13
+ };
@@ -0,0 +1,2 @@
1
+ export declare const i18nPlugin: (options?: import("./options.js").I18nPluginOptions) => import("@modern-js/runtime").RuntimePlugin;
2
+ export default i18nPlugin;
@@ -0,0 +1,2 @@
1
+ export declare const i18nPlugin: (options?: import("./options.js").I18nPluginOptions) => import("@modern-js/runtime").RuntimePlugin;
2
+ export default i18nPlugin;
@@ -0,0 +1,4 @@
1
+ import type { I18nPluginOptions as NativeI18nPluginOptions } from '@modern-js/plugin-i18n/runtime/no-react-i18next';
2
+ import type { RuntimePlugin } from '@modern-js/runtime';
3
+ import type { I18nPluginOptions } from './options.js';
4
+ export declare const composeI18nPlugin: (nativeI18nPlugin: (options: NativeI18nPluginOptions) => RuntimePlugin) => (options?: I18nPluginOptions) => RuntimePlugin;
@@ -0,0 +1,3 @@
1
+ import type { I18nServerPluginOptions } from './options.js';
2
+ export declare const i18nServerPlugin: (options: I18nServerPluginOptions) => import("@modern-js/server-core").ServerPlugin;
3
+ export default i18nServerPlugin;
@@ -0,0 +1,4 @@
1
+ import { type LocalisedUrlsOption } from '@modern-js/i18n-runtime-extensions';
2
+ import type { I18nUrlStrategy } from '@modern-js/plugin-i18n/runtime/no-react-i18next';
3
+ /** Each integration owns its map; no process-wide configuration is installed. */
4
+ export declare const createI18nUrlStrategy: (localisedUrls?: LocalisedUrlsOption) => I18nUrlStrategy;
package/package.json CHANGED
@@ -1,16 +1,150 @@
1
1
  {
2
2
  "name": "@bleedingdev/modern-js-i18n-integration",
3
- "version": "0.0.0",
4
- "description": "Package-name registration for the forthcoming UltraModern.js release. This setup version contains no runtime implementation.",
5
- "license": "MIT",
3
+ "description": "UltraModern.js localized routing and federated resource integration through native Modern.js i18n plugins.",
4
+ "homepage": "https://github.com/BleedingDev/ultramodern.js#readme",
5
+ "bugs": {
6
+ "url": "https://github.com/BleedingDev/ultramodern.js/issues"
7
+ },
6
8
  "repository": {
7
9
  "type": "git",
8
- "url": "git+https://github.com/BleedingDev/ultramodern.js.git"
10
+ "url": "git+https://github.com/BleedingDev/ultramodern.js.git",
11
+ "directory": "packages/runtime/i18n-integration"
9
12
  },
13
+ "license": "MIT",
14
+ "version": "3.9.0-ultramodern.10",
10
15
  "files": [
11
- "README.md"
16
+ "dist",
17
+ "src"
12
18
  ],
19
+ "engines": {
20
+ "node": ">=26.7.0"
21
+ },
22
+ "types": "./dist/types/cli.d.ts",
23
+ "main": "./dist/cjs/cli.js",
24
+ "exports": {
25
+ ".": {
26
+ "types": "./dist/types/cli.d.ts",
27
+ "node": {
28
+ "import": "./dist/esm-node/cli.mjs",
29
+ "require": "./dist/cjs/cli.js"
30
+ },
31
+ "default": "./dist/cjs/cli.js"
32
+ },
33
+ "./cli": {
34
+ "types": "./dist/types/cli.d.ts",
35
+ "node": {
36
+ "import": "./dist/esm-node/cli.mjs",
37
+ "require": "./dist/cjs/cli.js"
38
+ },
39
+ "default": "./dist/cjs/cli.js"
40
+ },
41
+ "./runtime": {
42
+ "types": "./dist/types/runtime.d.ts",
43
+ "node": {
44
+ "import": "./dist/esm-node/runtime.mjs",
45
+ "require": "./dist/cjs/runtime.js"
46
+ },
47
+ "default": "./dist/esm/runtime.mjs"
48
+ },
49
+ "./runtime/no-react-i18next": {
50
+ "types": "./dist/types/runtime-no-react-i18next.d.ts",
51
+ "node": {
52
+ "import": "./dist/esm-node/runtime-no-react-i18next.mjs",
53
+ "require": "./dist/cjs/runtime-no-react-i18next.js"
54
+ },
55
+ "default": "./dist/esm/runtime-no-react-i18next.mjs"
56
+ },
57
+ "./server": {
58
+ "types": "./dist/types/server.d.ts",
59
+ "node": {
60
+ "import": "./dist/esm-node/server.mjs",
61
+ "require": "./dist/cjs/server.js"
62
+ },
63
+ "default": "./dist/cjs/server.js"
64
+ },
65
+ "./federation": {
66
+ "types": "./dist/types/federation/boundary.d.ts",
67
+ "node": {
68
+ "import": "./dist/esm-node/federation/boundary.mjs",
69
+ "require": "./dist/cjs/federation/boundary.js"
70
+ },
71
+ "default": "./dist/esm/federation/boundary.mjs"
72
+ },
73
+ "./package.json": "./package.json",
74
+ "./options": {
75
+ "types": "./dist/types/options.d.ts",
76
+ "node": {
77
+ "import": "./dist/esm-node/options.mjs",
78
+ "require": "./dist/cjs/options.js"
79
+ },
80
+ "default": "./dist/esm/options.mjs"
81
+ }
82
+ },
83
+ "typesVersions": {
84
+ "*": {
85
+ ".": [
86
+ "./dist/types/cli.d.ts"
87
+ ],
88
+ "cli": [
89
+ "./dist/types/cli.d.ts"
90
+ ],
91
+ "runtime": [
92
+ "./dist/types/runtime.d.ts"
93
+ ],
94
+ "runtime/no-react-i18next": [
95
+ "./dist/types/runtime-no-react-i18next.d.ts"
96
+ ],
97
+ "server": [
98
+ "./dist/types/server.d.ts"
99
+ ],
100
+ "federation": [
101
+ "./dist/types/federation/boundary.d.ts"
102
+ ],
103
+ "options": [
104
+ "./dist/types/options.d.ts"
105
+ ]
106
+ }
107
+ },
108
+ "dependencies": {
109
+ "@modern-js/i18n-runtime-extensions": "npm:@bleedingdev/modern-js-i18n-runtime-extensions@3.9.0-ultramodern.10",
110
+ "@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.9.0-ultramodern.10",
111
+ "@modern-js/plugin-i18n": "npm:@bleedingdev/modern-js-plugin-i18n@3.9.0-ultramodern.10",
112
+ "@modern-js/runtime-extensions": "npm:@bleedingdev/modern-js-runtime-extensions@3.9.0-ultramodern.10",
113
+ "@modern-js/server-runtime": "npm:@bleedingdev/modern-js-server-runtime@3.9.0-ultramodern.10",
114
+ "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.9.0-ultramodern.10"
115
+ },
116
+ "peerDependencies": {
117
+ "@modern-js/app-tools": "3.9.0-ultramodern.10",
118
+ "@modern-js/runtime": "3.9.0-ultramodern.10",
119
+ "react": "^19.2.8"
120
+ },
121
+ "peerDependenciesMeta": {},
122
+ "optionalDependencies": {},
123
+ "devDependencies": {
124
+ "@modern-js/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.9.0-ultramodern.10",
125
+ "@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.9.0-ultramodern.10",
126
+ "@modern-js/tsconfig": "npm:@bleedingdev/modern-js-tsconfig@3.9.0-ultramodern.10",
127
+ "@rslib/core": "1.0.0",
128
+ "@rstest/core": "0.11.12",
129
+ "@scripts/rstest-config": "2.66.0",
130
+ "@types/node": "^26.4.1",
131
+ "@types/react": "^19.2.18",
132
+ "@types/react-dom": "^19.2.7",
133
+ "@typescript/native-preview": "7.0.0-dev.20260707.2",
134
+ "esbuild": "^0.28.2",
135
+ "i18next": "26.4.2",
136
+ "react": "^19.2.8",
137
+ "react-dom": "^19.2.8",
138
+ "react-i18next": "17.0.13",
139
+ "typescript": "^7.0.2"
140
+ },
141
+ "sideEffects": false,
13
142
  "publishConfig": {
14
143
  "access": "public"
144
+ },
145
+ "scripts": {
146
+ "dev": "rslib build --watch",
147
+ "build": "rslib build",
148
+ "test": "rstest"
15
149
  }
16
150
  }
package/src/cli.ts ADDED
@@ -0,0 +1,38 @@
1
+ import type { AppTools, CliPlugin } from '@modern-js/app-tools';
2
+
3
+ const runtimePaths = new Map([
4
+ ['@modern-js/plugin-i18n/runtime', '@modern-js/i18n-integration/runtime'],
5
+ [
6
+ '@modern-js/plugin-i18n/runtime/no-react-i18next',
7
+ '@modern-js/i18n-integration/runtime/no-react-i18next',
8
+ ],
9
+ ]);
10
+
11
+ /** Compose fork policy with the native plugin's own descriptor generation. */
12
+ export const ultramodernI18nIntegrationPlugin = (): CliPlugin<AppTools> => ({
13
+ name: '@modern-js/i18n-integration',
14
+ pre: ['@modern-js/plugin-i18n'],
15
+ setup(api) {
16
+ api._internalRuntimePlugins(({ entrypoint, plugins }) => ({
17
+ entrypoint,
18
+ plugins: plugins.map(plugin => {
19
+ const path = runtimePaths.get(plugin.path);
20
+ return path ? { ...plugin, path } : plugin;
21
+ }),
22
+ }));
23
+
24
+ // The native server plugin derives its URL policy from
25
+ // `localeDetection.localisedUrls` on its own, so the descriptor is left
26
+ // alone. Rewriting it to `@modern-js/i18n-integration/server` would make
27
+ // the server unbootable for an app that only declares
28
+ // `@modern-js/ultramodern-app-tools`.
29
+
30
+ // Localised route generation used to live here. It now ships in
31
+ // `@modern-js/plugin-i18n`'s own CLI plugin so a bare `appTools()`
32
+ // consumer gets it too; registering it in both places would expand the
33
+ // route tree twice. This plugin keeps only the fork's runtime-module
34
+ // swap, which is the part that genuinely needs the integration runtime.
35
+ },
36
+ });
37
+
38
+ export default ultramodernI18nIntegrationPlugin;