astro-react-i18next 0.1.3 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -39,14 +39,16 @@ export default defineConfig({
39
39
 
40
40
  The initialization function accepts an optional configuration object with the following options:
41
41
 
42
- | Option | Type | Description | Default |
43
- | --------------------- | ---------- | ---------------------------------------------------------------------------------- | ------------ |
44
- | `defaultLocale` | `string` | The default locale to use when no locale is specified. | `"en-US"` |
45
- | `locales` | `string[]` | An array of locales to support. | `["en-US"]` |
46
- | `defaultNamespace` | `string` | The default namespace to use when no namespace is specified. | `"common"` |
47
- | `namespaces` | `string[]` | An array of namespaces to support. | `["common"]` |
48
- | `prefixDefaultLocale` | `boolean` | Whether to prefix the default locale with the locale code. | `false` |
49
- | `localesDir` | `string` | The directory where the locale files are stored, relative to the public directory. | `"locales"` |
42
+ | Option | Type | Description | Default |
43
+ | --------------------- | ---------------------------------------------- | ---------------------------------------------------------------------------------- | ------------ |
44
+ | `defaultLocale` | `string` | The default locale to use when no locale is specified. | `"en-US"` |
45
+ | `locales` | `string[]` | An array of locales to support. | `["en-US"]` |
46
+ | `defaultNamespace` | `string` | The default namespace to use when no namespace is specified. | `"common"` |
47
+ | `namespaces` | `string[]` | An array of namespaces to support. | `["common"]` |
48
+ | `prefixDefaultLocale` | `boolean` | Whether to prefix the default locale with the locale code. | `false` |
49
+ | `localesDir` | `string` | The directory where the locale files are stored, relative to the public directory. | `"locales"` |
50
+ | `domains` | `{ domain: string; defaultLocale: string; }[]` | An array of domains for language selection. | `[]` |
51
+ | `reservedRoutes` | `string[]` | An array of routes excluded from locale handling. | `["/api"]` |
50
52
 
51
53
  Here is an example of how to configure the integration:
52
54
 
@@ -62,8 +64,6 @@ export default defineConfig({
62
64
  + reactI18next({
63
65
  + defaultLocale: "en-US",
64
66
  + locales: ["en-US", "fr-FR", "zh-TW"],
65
- + defaultNamespace: "app",
66
- + namespaces: ["app"],
67
67
  + }),
68
68
  ],
69
69
  });
@@ -83,8 +83,6 @@ export default defineConfig({
83
83
  reactI18next({
84
84
  defaultLocale: "en-US",
85
85
  locales: ["en-US", "fr-FR", "zh-TW"],
86
- defaultNamespace: "app",
87
- namespaces: ["app"],
88
86
  }),
89
87
  ],
90
88
  + output: "server",
@@ -189,12 +187,33 @@ The integration provides utility functions to help manage locales and translatio
189
187
 
190
188
  All utility functions are available in the `astro-react-i18next/utils` module.
191
189
 
192
- | Function | Description | Returns |
193
- | -------------------------------------------------- | -------------------------------------------------------- | ---------------------------------------------------------------------------- |
194
- | `getLocaleConfig()` | Returns the locale configuration object. | `{ defaultLocale: string, locales: string[], prefixDefaultLocale: boolean }` |
195
- | `getLocalizedPathname(pathname = "", locale = "")` | Returns the localized pathname for the specified locale. | string |
196
- | `buildStaticPaths()` | Generates static paths for each locale. | `{ params: { locale: string \| undefined; }; }[]` |
197
- | `changeLocale(nextLocale = "", shallow = true)` | Changes the current locale. | |
190
+ | Function | Description | Returns |
191
+ | -------------------------------------------------- | -------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
192
+ | `getLocaleConfig()` | Returns the locale configuration object. | `{ defaultLocale: string; locales: string[]; prefixDefaultLocale: boolean; domains: { domain: string; defaultLocale: string; }[]; reservedRoutes: string[]; }` |
193
+ | `getLocalizedPathname(pathname = "", locale = "")` | Returns the localized pathname for the specified locale. | `string` |
194
+ | `buildStaticPaths()` | Generates static paths for each locale. | `{ params: { locale: string \| undefined; }; }[]` |
195
+ | `changeLocale(nextLocale = "", shallow = true)` | Changes the current locale. | |
196
+
197
+ ## Developing locally
198
+
199
+ Clone the repository:
200
+
201
+ ```bash
202
+ git clone https://github.com/jeremyxgo/astro-react-i18next.git
203
+ cd astro-react-i18next
204
+ ```
205
+
206
+ Build the package:
207
+
208
+ ```bash
209
+ npm run build
210
+ ```
211
+
212
+ Install the package in your project:
213
+
214
+ ```bash
215
+ npm install $(npm pack /path/to/astro-react-i18next | tail -1)
216
+ ```
198
217
 
199
218
  ## License
200
219
 
package/dist/index.d.ts CHANGED
@@ -6,13 +6,18 @@ interface AstroReactI18nextOptions {
6
6
  namespaces?: string[];
7
7
  prefixDefaultLocale?: boolean;
8
8
  localesDir?: string;
9
+ domains?: {
10
+ domain: string;
11
+ defaultLocale: string;
12
+ }[];
13
+ reservedRoutes?: string[];
9
14
  }
10
15
  type MergedAstroReactI18nextOptions = {
11
16
  [key in keyof AstroReactI18nextOptions]-?: AstroReactI18nextOptions[key];
12
17
  };
13
18
  declare module "i18next" {
14
19
  interface InitOptions {
15
- astroReactI18next: MergedAstroReactI18nextOptions;
20
+ astroReactI18next: Pick<MergedAstroReactI18nextOptions, "defaultLocale" | "locales" | "prefixDefaultLocale" | "domains" | "reservedRoutes">;
16
21
  }
17
22
  }
18
23
  export default function AstroReactI18nextIntegration(options?: AstroReactI18nextOptions): AstroIntegration;
package/dist/index.js CHANGED
@@ -7,17 +7,15 @@ var DEFAULT_OPTIONS = {
7
7
  defaultNamespace: "common",
8
8
  namespaces: ["common"],
9
9
  prefixDefaultLocale: false,
10
- localesDir: "locales"
10
+ localesDir: "locales",
11
+ domains: [],
12
+ reservedRoutes: ["/api"]
11
13
  };
12
14
  function buildI18nextInitScript({
13
15
  backendType,
14
16
  basePath,
15
- options
17
+ mergedOptions
16
18
  }) {
17
- const mergedOptions = {
18
- ...DEFAULT_OPTIONS,
19
- ...options
20
- };
21
19
  let imports = "";
22
20
  let i18nextPlugins = "";
23
21
  let i18nextOptions = "";
@@ -64,13 +62,47 @@ function buildI18nextInitScript({
64
62
  escapeValue: false,
65
63
  },
66
64
  backend: {
67
- loadPath: "${path.join(basePath, mergedOptions.localesDir)}/{{lng}}/{{ns}}.json",
65
+ loadPath: "${path.join(basePath, mergedOptions.localesDir || "")}/{{lng}}/{{ns}}.json",
68
66
  },
69
- astroReactI18next: ${JSON.stringify(mergedOptions)},
67
+ astroReactI18next: ${JSON.stringify({
68
+ defaultLocale: mergedOptions.defaultLocale,
69
+ locales: mergedOptions.locales,
70
+ prefixDefaultLocale: mergedOptions.prefixDefaultLocale,
71
+ domains: mergedOptions.domains,
72
+ reservedRoutes: mergedOptions.reservedRoutes
73
+ })},
70
74
  ${i18nextOptions}
71
75
  });
72
76
  `;
73
77
  }
78
+ function buildLocaleRestorationScript(mergedOptions) {
79
+ return `
80
+ window.addEventListener("DOMContentLoaded", () => {
81
+ const defaultLocale = "${mergedOptions.defaultLocale}";
82
+ const locales = ${JSON.stringify(mergedOptions.locales)};
83
+ const reservedRoutes = ${JSON.stringify(mergedOptions.reservedRoutes)};
84
+ const pathname = window.location.pathname;
85
+
86
+ if (
87
+ reservedRoutes.some(
88
+ (route) =>
89
+ pathname === route ||
90
+ pathname.startsWith(route + "/"),
91
+ )
92
+ ) {
93
+ return;
94
+ }
95
+
96
+ let detectedLocale = pathname.split("/")[1];
97
+
98
+ if (!locales.includes(detectedLocale)) {
99
+ detectedLocale = defaultLocale;
100
+ }
101
+
102
+ i18n.changeLanguage(detectedLocale);
103
+ });
104
+ `;
105
+ }
74
106
  function AstroReactI18nextIntegration(options) {
75
107
  const mergedOptions = {
76
108
  ...DEFAULT_OPTIONS,
@@ -85,39 +117,29 @@ function AstroReactI18nextIntegration(options) {
85
117
  injectScript,
86
118
  updateConfig
87
119
  }) => {
88
- const clientLocaleRestorationScript = `
89
- window.addEventListener("DOMContentLoaded", () => {
90
- const defaultLocale = "${mergedOptions.defaultLocale}";
91
- const locales = ${JSON.stringify(mergedOptions.locales)};
92
- let detectedLocale = window.location.pathname.split("/")[1];
93
-
94
- if (!locales.includes(detectedLocale)) {
95
- detectedLocale = defaultLocale;
96
- }
97
-
98
- i18n.changeLanguage(detectedLocale);
99
- });
100
- `;
120
+ const middlewareEntrypoint = config.output === "server" ? `${INTEGRATION_NAME}/middleware/server` : `${INTEGRATION_NAME}/middleware/static`;
101
121
  const clientI18nextInitScript = buildI18nextInitScript({
102
122
  backendType: "http",
103
123
  basePath: "/",
104
- options
124
+ mergedOptions
105
125
  });
106
126
  const serverI18nextInitScript = buildI18nextInitScript({
107
127
  backendType: "fs",
108
128
  basePath: config.publicDir.pathname,
109
- options
129
+ mergedOptions
110
130
  });
111
- const middlewareEntrypoint = config.output === "server" ? `${INTEGRATION_NAME}/middleware/server` : `${INTEGRATION_NAME}/middleware/static`;
131
+ const localeRestorationScript = buildLocaleRestorationScript(mergedOptions);
112
132
  addMiddleware({
113
133
  entrypoint: middlewareEntrypoint,
114
134
  order: "post"
115
135
  });
116
136
  injectScript("page-ssr", serverI18nextInitScript);
117
137
  injectScript("before-hydration", clientI18nextInitScript);
118
- injectScript("before-hydration", clientLocaleRestorationScript);
119
138
  injectScript("page", clientI18nextInitScript);
120
- injectScript("page", clientLocaleRestorationScript);
139
+ if (mergedOptions.domains.length === 0) {
140
+ injectScript("before-hydration", localeRestorationScript);
141
+ injectScript("page", localeRestorationScript);
142
+ }
121
143
  updateConfig({
122
144
  i18n: {
123
145
  locales: mergedOptions.locales,
@@ -4,8 +4,9 @@ import i18n2 from "i18next";
4
4
  // src/utils.ts
5
5
  import i18n from "i18next";
6
6
  function getLocaleConfig() {
7
- const { defaultLocale, locales, prefixDefaultLocale } = i18n.options.astroReactI18next;
8
- return { defaultLocale, locales, prefixDefaultLocale };
7
+ return JSON.parse(
8
+ JSON.stringify(i18n.options.astroReactI18next)
9
+ );
9
10
  }
10
11
  function getLocalizedPathname(pathname = "", locale = "") {
11
12
  const { defaultLocale, locales, prefixDefaultLocale } = getLocaleConfig();
@@ -21,12 +22,23 @@ function getLocalizedPathname(pathname = "", locale = "") {
21
22
  }
22
23
 
23
24
  // src/middleware-server.ts
25
+ var ASTRO_RESERVED_ROUTES = ["/_astro", "/_actions", "/_server-islands"];
24
26
  async function onRequest(context, next) {
25
- const { defaultLocale, locales } = getLocaleConfig();
27
+ const { defaultLocale, locales, domains, reservedRoutes } = getLocaleConfig();
28
+ if ([...ASTRO_RESERVED_ROUTES, ...reservedRoutes].some(
29
+ (route) => context.url.pathname === route || context.url.pathname.startsWith(route + "/")
30
+ )) {
31
+ return next();
32
+ }
33
+ const localesByDomain = Object.fromEntries(
34
+ domains.map((domain) => [domain.domain, domain.defaultLocale])
35
+ );
36
+ const localeFromDomain = localesByDomain[context.url.host];
26
37
  const localeFromPathname = context.url.pathname.split("/")[1];
27
38
  const localeFromCookie = context.cookies.get("i18next")?.value;
28
39
  const localeFromHeader = context.preferredLocale;
29
40
  const nextLocale = [
41
+ localeFromDomain,
30
42
  localeFromPathname,
31
43
  localeFromCookie,
32
44
  localeFromHeader,
@@ -36,7 +48,7 @@ async function onRequest(context, next) {
36
48
  context.cookies.set("i18next", nextLocale || "", { path: "/" });
37
49
  const { hash, pathname, search } = context.url;
38
50
  const nextPathname = getLocalizedPathname(pathname, nextLocale);
39
- if (nextPathname !== pathname) {
51
+ if (nextPathname !== pathname && domains.length === 0) {
40
52
  const nextUrl = nextPathname + search + hash;
41
53
  return context.redirect(nextUrl);
42
54
  }
@@ -4,8 +4,9 @@ import i18n2 from "i18next";
4
4
  // src/utils.ts
5
5
  import i18n from "i18next";
6
6
  function getLocaleConfig() {
7
- const { defaultLocale, locales, prefixDefaultLocale } = i18n.options.astroReactI18next;
8
- return { defaultLocale, locales, prefixDefaultLocale };
7
+ return JSON.parse(
8
+ JSON.stringify(i18n.options.astroReactI18next)
9
+ );
9
10
  }
10
11
 
11
12
  // src/middleware-static.ts
package/dist/utils.d.ts CHANGED
@@ -1,8 +1,5 @@
1
- export declare function getLocaleConfig(): {
2
- defaultLocale: string;
3
- locales: string[];
4
- prefixDefaultLocale: boolean;
5
- };
1
+ import i18n from "i18next";
2
+ export declare function getLocaleConfig(): typeof i18n.options.astroReactI18next;
6
3
  export declare function getLocalizedPathname(pathname?: string, locale?: string): string;
7
4
  export declare function buildStaticPaths(): {
8
5
  params: {
package/dist/utils.js CHANGED
@@ -1,8 +1,9 @@
1
1
  // src/utils.ts
2
2
  import i18n from "i18next";
3
3
  function getLocaleConfig() {
4
- const { defaultLocale, locales, prefixDefaultLocale } = i18n.options.astroReactI18next;
5
- return { defaultLocale, locales, prefixDefaultLocale };
4
+ return JSON.parse(
5
+ JSON.stringify(i18n.options.astroReactI18next)
6
+ );
6
7
  }
7
8
  function getLocalizedPathname(pathname = "", locale = "") {
8
9
  const { defaultLocale, locales, prefixDefaultLocale } = getLocaleConfig();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro-react-i18next",
3
- "version": "0.1.3",
3
+ "version": "0.3.0",
4
4
  "description": "Integrates i18next and react-i18next seamlessly into your Astro website to provide robust i18n support for React components.",
5
5
  "keywords": [
6
6
  "astro-integration",