next-intlayer 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/LICENSE +21 -0
  2. package/dist/cjs/client/index.cjs +48 -0
  3. package/dist/cjs/client/index.cjs.map +1 -0
  4. package/dist/cjs/client/index.d.ts +10 -0
  5. package/dist/cjs/client/useLocale.cjs +68 -0
  6. package/dist/cjs/client/useLocale.cjs.map +1 -0
  7. package/dist/cjs/client/useLocale.d.ts +12 -0
  8. package/dist/cjs/generateStaticParams.cjs +36 -0
  9. package/dist/cjs/generateStaticParams.cjs.map +1 -0
  10. package/dist/cjs/generateStaticParams.d.ts +7 -0
  11. package/dist/cjs/index.cjs +51 -0
  12. package/dist/cjs/index.cjs.map +1 -0
  13. package/dist/cjs/index.d.ts +15 -0
  14. package/dist/cjs/middleware/index.cjs +35 -0
  15. package/dist/cjs/middleware/index.cjs.map +1 -0
  16. package/dist/cjs/middleware/index.d.ts +2 -0
  17. package/dist/cjs/middleware/intlayerMiddleware.cjs +230 -0
  18. package/dist/cjs/middleware/intlayerMiddleware.cjs.map +1 -0
  19. package/dist/cjs/middleware/intlayerMiddleware.d.ts +5 -0
  20. package/dist/cjs/middleware/localeDetector.cjs +73 -0
  21. package/dist/cjs/middleware/localeDetector.cjs.map +1 -0
  22. package/dist/cjs/middleware/localeDetector.d.ts +6 -0
  23. package/dist/cjs/server/index.cjs +47 -0
  24. package/dist/cjs/server/index.cjs.map +1 -0
  25. package/dist/cjs/server/index.d.ts +10 -0
  26. package/dist/cjs/server/withIntlayer.cjs +76 -0
  27. package/dist/cjs/server/withIntlayer.cjs.map +1 -0
  28. package/dist/cjs/server/withIntlayer.d.ts +9 -0
  29. package/dist/cjs/types/NextPage.cjs +21 -0
  30. package/dist/cjs/types/NextPage.cjs.map +1 -0
  31. package/dist/cjs/types/NextPage.d.ts +13 -0
  32. package/dist/cjs/types/index.cjs +21 -0
  33. package/dist/cjs/types/index.cjs.map +1 -0
  34. package/dist/cjs/types/index.d.ts +4 -0
  35. package/dist/esm/client/index.d.mts +10 -0
  36. package/dist/esm/client/index.mjs +19 -0
  37. package/dist/esm/client/index.mjs.map +1 -0
  38. package/dist/esm/client/useLocale.d.mts +12 -0
  39. package/dist/esm/client/useLocale.mjs +37 -0
  40. package/dist/esm/client/useLocale.mjs.map +1 -0
  41. package/dist/esm/generateStaticParams.d.mts +7 -0
  42. package/dist/esm/generateStaticParams.mjs +5 -0
  43. package/dist/esm/generateStaticParams.mjs.map +1 -0
  44. package/dist/esm/index.d.mts +15 -0
  45. package/dist/esm/index.mjs +21 -0
  46. package/dist/esm/index.mjs.map +1 -0
  47. package/dist/esm/middleware/index.d.mts +2 -0
  48. package/dist/esm/middleware/index.mjs +2 -0
  49. package/dist/esm/middleware/index.mjs.map +1 -0
  50. package/dist/esm/middleware/intlayerMiddleware.d.mts +5 -0
  51. package/dist/esm/middleware/intlayerMiddleware.mjs +193 -0
  52. package/dist/esm/middleware/intlayerMiddleware.mjs.map +1 -0
  53. package/dist/esm/middleware/localeDetector.d.mts +6 -0
  54. package/dist/esm/middleware/localeDetector.mjs +20 -0
  55. package/dist/esm/middleware/localeDetector.mjs.map +1 -0
  56. package/dist/esm/server/index.d.mts +10 -0
  57. package/dist/esm/server/index.mjs +19 -0
  58. package/dist/esm/server/index.mjs.map +1 -0
  59. package/dist/esm/server/withIntlayer.d.mts +9 -0
  60. package/dist/esm/server/withIntlayer.mjs +40 -0
  61. package/dist/esm/server/withIntlayer.mjs.map +1 -0
  62. package/dist/esm/types/NextPage.d.mts +13 -0
  63. package/dist/esm/types/NextPage.mjs +1 -0
  64. package/dist/esm/types/NextPage.mjs.map +1 -0
  65. package/dist/esm/types/index.d.mts +4 -0
  66. package/dist/esm/types/index.mjs +1 -0
  67. package/dist/esm/types/index.mjs.map +1 -0
  68. package/package.json +105 -0
  69. package/src/client/index.ts +9 -0
  70. package/src/client/useLocale.ts +46 -0
  71. package/src/generateStaticParams.ts +5 -0
  72. package/src/index.ts +11 -0
  73. package/src/middleware/index.ts +1 -0
  74. package/src/middleware/intlayerMiddleware.ts +215 -0
  75. package/src/middleware/localeDetector.ts +28 -0
  76. package/src/server/index.ts +9 -0
  77. package/src/server/withIntlayer.ts +61 -0
  78. package/src/types/NextPage.ts +10 -0
  79. package/src/types/index.ts +1 -0
@@ -0,0 +1,20 @@
1
+ import { match } from "@formatjs/intl-localematcher";
2
+ import { getConfiguration } from "@intlayer/config/client";
3
+ import Negotiator from "negotiator";
4
+ const { locales, defaultLocale } = getConfiguration().internationalization;
5
+ const localeDetector = (request) => {
6
+ const negotiatorHeaders = {};
7
+ request.headers.forEach((value, key) => (negotiatorHeaders[key] = value));
8
+ const languages = new Negotiator({ headers: negotiatorHeaders }).languages();
9
+ try {
10
+ return match(languages, locales, defaultLocale);
11
+ } catch (e) {
12
+ console.warn(
13
+ `No valid locales in accept-language header: ${languages.join(", ")}`
14
+ );
15
+ console.warn(`Reverting to using defaultLocale: ${defaultLocale}`);
16
+ return defaultLocale;
17
+ }
18
+ };
19
+ export { localeDetector };
20
+ //# sourceMappingURL=localeDetector.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/middleware/localeDetector.ts"],"sourcesContent":["import { match } from '@formatjs/intl-localematcher';\nimport type { Locales } from '@intlayer/config';\nimport { getConfiguration } from '@intlayer/config/client';\nimport Negotiator from 'negotiator';\nimport type { NextRequest } from 'next/server.js';\n\nconst { locales, defaultLocale } = getConfiguration().internationalization;\n\nexport const localeDetector = (request: NextRequest): Locales => {\n const negotiatorHeaders: Record<string, string> = {};\n\n request.headers.forEach((value, key) => (negotiatorHeaders[key] = value));\n\n const languages = new Negotiator({ headers: negotiatorHeaders }).languages();\n\n // match can only use specifically formatted locales\n // https://stackoverflow.com/questions/76447732/nextjs-13-i18n-incorrect-locale-information-provided\n try {\n return match(languages, locales, defaultLocale) as Locales;\n } catch (e) {\n console.warn(\n `No valid locales in accept-language header: ${languages.join(', ')}`\n );\n console.warn(`Reverting to using defaultLocale: ${defaultLocale}`);\n\n return defaultLocale;\n }\n};\n"],"mappings":"AAAA,SAAS,aAAa;AAEtB,SAAS,wBAAwB;AACjC,OAAO,gBAAgB;AAGvB,MAAM,EAAE,SAAS,cAAc,IAAI,iBAAiB,EAAE;AAE/C,MAAM,iBAAiB,CAAC,YAAkC;AAC/D,QAAM,oBAA4C,CAAC;AAEnD,UAAQ,QAAQ,QAAQ,CAAC,OAAO,QAAS,kBAAkB,GAAG,IAAI,KAAM;AAExE,QAAM,YAAY,IAAI,WAAW,EAAE,SAAS,kBAAkB,CAAC,EAAE,UAAU;AAI3E,MAAI;AACF,WAAO,MAAM,WAAW,SAAS,aAAa;AAAA,EAChD,SAAS,GAAG;AACV,YAAQ;AAAA,MACN,+CAA+C,UAAU,KAAK,IAAI,CAAC;AAAA,IACrE;AACA,YAAQ,KAAK,qCAAqC,aAAa,EAAE;AAEjE,WAAO;AAAA,EACT;AACF;","names":[]}
@@ -0,0 +1,10 @@
1
+ export {
2
+ LocaleServerContext,
3
+ LocaleServerContextProvider,
4
+ getLocaleContent,
5
+ locale,
6
+ useIntlayer,
7
+ useTraduction,
8
+ } from "react-intlayer/server";
9
+ export { withIntlayer } from "./withIntlayer.mjs";
10
+ import "next";
@@ -0,0 +1,19 @@
1
+ import {
2
+ getLocaleContent,
3
+ useTraduction,
4
+ LocaleServerContext,
5
+ LocaleServerContextProvider,
6
+ locale,
7
+ useIntlayer,
8
+ } from "react-intlayer/server";
9
+ import { withIntlayer } from "./withIntlayer.mjs";
10
+ export {
11
+ LocaleServerContext,
12
+ LocaleServerContextProvider,
13
+ getLocaleContent,
14
+ locale,
15
+ useIntlayer,
16
+ useTraduction,
17
+ withIntlayer,
18
+ };
19
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/server/index.ts"],"sourcesContent":["export {\n getLocaleContent,\n useTraduction,\n LocaleServerContext,\n LocaleServerContextProvider,\n locale,\n useIntlayer,\n} from 'react-intlayer/server';\nexport { withIntlayer } from './withIntlayer';\n"],"mappings":"AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,oBAAoB;","names":[]}
@@ -0,0 +1,9 @@
1
+ import "react-intlayer/server";
2
+ import { NextConfig } from "next";
3
+
4
+ type PluginOptions = {};
5
+ declare const withIntlayer: (
6
+ _pluginOptions?: PluginOptions
7
+ ) => (nextConfig?: Partial<NextConfig>) => Partial<NextConfig>;
8
+
9
+ export { withIntlayer };
@@ -0,0 +1,40 @@
1
+ import { resolve, relative, join } from "path";
2
+ import { getConfiguration, formatEnvVariable } from "@intlayer/config";
3
+ const intlayerConfig = getConfiguration({
4
+ verbose: true,
5
+ });
6
+ const env = formatEnvVariable("NEXT_PUBLIC_INTLAYER_");
7
+ const { mainDir, baseDir } = intlayerConfig.content;
8
+ const mergeEnvVariable = (nextEnv = {}) => Object.assign({}, nextEnv, env);
9
+ const mergeStats = (nextStats = {}) =>
10
+ Object.assign({}, nextStats, {
11
+ warnings: false,
12
+ });
13
+ const withIntlayer =
14
+ (_pluginOptions = {}) =>
15
+ (nextConfig = {}) => {
16
+ if (typeof nextConfig !== "object") nextConfig = {};
17
+ return Object.assign({}, nextConfig, {
18
+ env: mergeEnvVariable(nextConfig.env),
19
+ stats: mergeStats(nextConfig.stats),
20
+ webpack: (config) => {
21
+ const dictionariesPath = join(mainDir, "dictionaries.cjs");
22
+ const relativeDictionariesPath = relative(baseDir, dictionariesPath);
23
+ config.externals.push({
24
+ esbuild: "esbuild",
25
+ module: "module",
26
+ fs: "fs",
27
+ });
28
+ config.resolve.alias["@intlayer/dictionaries-entry"] = resolve(
29
+ relativeDictionariesPath
30
+ );
31
+ config.module.rules.push({
32
+ test: /\.node$/,
33
+ loader: "node-loader",
34
+ });
35
+ return config;
36
+ },
37
+ });
38
+ };
39
+ export { withIntlayer };
40
+ //# sourceMappingURL=withIntlayer.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/server/withIntlayer.ts"],"sourcesContent":["import { resolve, relative, join } from 'path';\nimport { getConfiguration, formatEnvVariable } from '@intlayer/config';\nimport type { NextConfig } from 'next';\nimport type { NextJsWebpackConfig } from 'next/dist/server/config-shared';\n\ntype PluginOptions = {\n // TODO: add options\n};\n\nconst intlayerConfig = getConfiguration({\n verbose: true,\n});\n\n// Set all configuration values as environment variables\nconst env = formatEnvVariable('NEXT_PUBLIC_INTLAYER_');\nconst { mainDir, baseDir } = intlayerConfig.content;\n\nconst mergeEnvVariable = (\n nextEnv: Record<string, unknown> | undefined = {}\n): Record<string, string> => Object.assign({}, nextEnv, env);\n\nconst mergeStats = (\n nextStats: Record<string, unknown> | undefined = {}\n): Record<string, unknown> =>\n Object.assign({}, nextStats, {\n warnings: false,\n });\n\nexport const withIntlayer =\n (_pluginOptions: PluginOptions = {}) =>\n (nextConfig: Partial<NextConfig> = {}): Partial<NextConfig> => {\n if (typeof nextConfig !== 'object') nextConfig = {};\n\n return Object.assign({}, nextConfig, {\n env: mergeEnvVariable(nextConfig.env),\n\n stats: mergeStats(nextConfig.stats),\n\n webpack: (config: Parameters<NextJsWebpackConfig>['0']) => {\n const dictionariesPath = join(mainDir, 'dictionaries.cjs');\n const relativeDictionariesPath = relative(baseDir, dictionariesPath);\n\n config.externals.push({\n esbuild: 'esbuild',\n module: 'module',\n fs: 'fs',\n });\n\n config.resolve.alias['@intlayer/dictionaries-entry'] = resolve(\n relativeDictionariesPath\n );\n\n config.module.rules.push({\n test: /\\.node$/,\n loader: 'node-loader',\n });\n\n return config;\n },\n });\n };\n"],"mappings":"AAAA,SAAS,SAAS,UAAU,YAAY;AACxC,SAAS,kBAAkB,yBAAyB;AAQpD,MAAM,iBAAiB,iBAAiB;AAAA,EACtC,SAAS;AACX,CAAC;AAGD,MAAM,MAAM,kBAAkB,uBAAuB;AACrD,MAAM,EAAE,SAAS,QAAQ,IAAI,eAAe;AAE5C,MAAM,mBAAmB,CACvB,UAA+C,CAAC,MACrB,OAAO,OAAO,CAAC,GAAG,SAAS,GAAG;AAE3D,MAAM,aAAa,CACjB,YAAiD,CAAC,MAElD,OAAO,OAAO,CAAC,GAAG,WAAW;AAAA,EAC3B,UAAU;AACZ,CAAC;AAEI,MAAM,eACX,CAAC,iBAAgC,CAAC,MAClC,CAAC,aAAkC,CAAC,MAA2B;AAC7D,MAAI,OAAO,eAAe;AAAU,iBAAa,CAAC;AAElD,SAAO,OAAO,OAAO,CAAC,GAAG,YAAY;AAAA,IACnC,KAAK,iBAAiB,WAAW,GAAG;AAAA,IAEpC,OAAO,WAAW,WAAW,KAAK;AAAA,IAElC,SAAS,CAAC,WAAiD;AACzD,YAAM,mBAAmB,KAAK,SAAS,kBAAkB;AACzD,YAAM,2BAA2B,SAAS,SAAS,gBAAgB;AAEnE,aAAO,UAAU,KAAK;AAAA,QACpB,SAAS;AAAA,QACT,QAAQ;AAAA,QACR,IAAI;AAAA,MACN,CAAC;AAED,aAAO,QAAQ,MAAM,8BAA8B,IAAI;AAAA,QACrD;AAAA,MACF;AAEA,aAAO,OAAO,MAAM,KAAK;AAAA,QACvB,MAAM;AAAA,QACN,QAAQ;AAAA,MACV,CAAC;AAED,aAAO;AAAA,IACT;AAAA,EACF,CAAC;AACH;","names":[]}
@@ -0,0 +1,13 @@
1
+ import { Locales } from "intlayer";
2
+ import { NextPage } from "next";
3
+ import { PropsWithChildren } from "react";
4
+
5
+ type LocalParams = {
6
+ params: {
7
+ locale: Locales;
8
+ };
9
+ };
10
+ type NextPageIntlayer = NextPage<LocalParams>;
11
+ type NextLayoutIntlayer = NextPage<PropsWithChildren<LocalParams>>;
12
+
13
+ export type { LocalParams, NextLayoutIntlayer, NextPageIntlayer };
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=NextPage.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,4 @@
1
+ export { LocalParams, NextPageIntlayer } from "./NextPage.mjs";
2
+ import "intlayer";
3
+ import "next";
4
+ import "react";
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/package.json ADDED
@@ -0,0 +1,105 @@
1
+ {
2
+ "name": "next-intlayer",
3
+ "version": "1.0.0",
4
+ "private": false,
5
+ "description": "Webpack configuration for IntLayer using NextJS",
6
+ "keywords": [
7
+ "intlayer",
8
+ "webpack",
9
+ "application",
10
+ "transpile",
11
+ "nextjs",
12
+ "typescript",
13
+ "javascript",
14
+ "json",
15
+ "file"
16
+ ],
17
+ "homepage": "https://github.com/aypineau/intlayer",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "https://github.com/aypineau/intlayer"
21
+ },
22
+ "license": "MIT",
23
+ "author": {
24
+ "name": "Aymeric PINEAU",
25
+ "url": "https://github.com/aypineau"
26
+ },
27
+ "exports": {
28
+ ".": {
29
+ "types": "./dist/esm/index.d.mts",
30
+ "require": "./dist/cjs/index.cjs",
31
+ "import": "./dist/esm/index.mjs"
32
+ },
33
+ "./client": {
34
+ "types": "./dist/esm/client/index.d.mts",
35
+ "require": "./dist/cjs/client/index.cjs",
36
+ "import": "./dist/esm/client/index.mjs"
37
+ },
38
+ "./middleware": {
39
+ "types": "./dist/esm/middleware/index.d.mts",
40
+ "require": "./dist/cjs/middleware/index.cjs",
41
+ "import": "./dist/esm/middleware/index.mjs"
42
+ },
43
+ "./server": {
44
+ "types": "./dist/esm/server/index.d.mts",
45
+ "require": "./dist/cjs/server/index.cjs",
46
+ "import": "./dist/esm/server/index.mjs"
47
+ },
48
+ "./package.json": "./package.json"
49
+ },
50
+ "main": "src/index.ts",
51
+ "typesVersions": {
52
+ "*": {
53
+ "package.json": [
54
+ "./package.json"
55
+ ]
56
+ }
57
+ },
58
+ "files": [
59
+ "./dist",
60
+ "./src",
61
+ "./bin",
62
+ "./package.json"
63
+ ],
64
+ "dependencies": {
65
+ "@formatjs/intl-localematcher": "^0.5.4",
66
+ "negotiator": "^0.6.3",
67
+ "next": "14.1.4",
68
+ "webpack": "^5.91.0",
69
+ "@intlayer/core": "^1.0.0",
70
+ "@intlayer/config": "^1.0.0",
71
+ "intlayer": "^1.0.0",
72
+ "@intlayer/dictionaries-entry": "^1.0.0",
73
+ "react-intlayer": "^1.0.0"
74
+ },
75
+ "devDependencies": {
76
+ "@types/negotiator": "^0.6.3",
77
+ "@types/node": "^20.11.30",
78
+ "@types/react": "^18.2.78",
79
+ "react": "^18.2.0",
80
+ "rimraf": "5.0.5",
81
+ "tsup": "^8.0.2",
82
+ "typescript": "^5.4.3",
83
+ "@utils/eslint-config": "^1.0.0",
84
+ "@utils/ts-config": "^1.0.0"
85
+ },
86
+ "peerDependencies": {
87
+ "react": "^18.2.0"
88
+ },
89
+ "engines": {
90
+ "node": ">=14.18"
91
+ },
92
+ "bug": {
93
+ "url": "https://github.com/aypineau/intlayer/issues"
94
+ },
95
+ "scripts": {
96
+ "build": "tsup",
97
+ "clean": "rimraf ./dist",
98
+ "dev": "tsup --watch",
99
+ "lint": "eslint . --ext .ts,.tsx,.js,.jsx,.cjs,.mjs",
100
+ "lint:fix": "eslint . --ext .ts,.tsx,.js,.jsx,.cjs,.mjs --fix",
101
+ "prettier:fix": "prettier --write src/**/*",
102
+ "test": "",
103
+ "typecheck": "tsup--project ./tsconfig.json --noEmit"
104
+ }
105
+ }
@@ -0,0 +1,9 @@
1
+ export {
2
+ getTranslation,
3
+ LocaleClientContextProvider,
4
+ LocaleClientContext,
5
+ useIntlayer,
6
+ useTraduction,
7
+ useLocaleCookie,
8
+ } from 'react-intlayer';
9
+ export { useLocale } from './useLocale';
@@ -0,0 +1,46 @@
1
+ import { intlayerMiddlewareConfiguration } from '@intlayer/config/client';
2
+ import type { Locales } from 'intlayer';
3
+ import { usePathname, useRouter } from 'next/navigation.js';
4
+ import { useLocale as useReactLocale, useLocaleCookie } from 'react-intlayer';
5
+
6
+ const { prefixDefault } = intlayerMiddlewareConfiguration;
7
+
8
+ export const useLocale = () => {
9
+ const { setLocaleCookie } = useLocaleCookie();
10
+ const reactLocaleHook = useReactLocale();
11
+ const router = useRouter();
12
+ const pathname = usePathname();
13
+
14
+ const {
15
+ defaultLocale,
16
+ availableLocales,
17
+ locale: currentLocale,
18
+ } = reactLocaleHook;
19
+
20
+ const setLocale = (locale: Locales) => {
21
+ if (currentLocale === locale) return;
22
+
23
+ if (!availableLocales.includes(locale)) {
24
+ console.error(`Locale ${locale} is not available`);
25
+ return;
26
+ }
27
+
28
+ setLocaleCookie(locale);
29
+
30
+ const pathWithoutLocale =
31
+ !prefixDefault && currentLocale === defaultLocale
32
+ ? pathname
33
+ : pathname.slice(`/${currentLocale}`.length) || '/';
34
+
35
+ if (!prefixDefault && locale === defaultLocale) {
36
+ return router.push(pathWithoutLocale);
37
+ }
38
+
39
+ return router.push(`/${locale}${pathWithoutLocale}`);
40
+ };
41
+
42
+ return {
43
+ ...reactLocaleHook,
44
+ setLocale,
45
+ };
46
+ };
@@ -0,0 +1,5 @@
1
+ import { intlayerIntlConfiguration } from '@intlayer/config/client';
2
+
3
+ const { locales } = intlayerIntlConfiguration;
4
+
5
+ export const generateStaticParams = () => locales.map((locale) => ({ locale }));
package/src/index.ts ADDED
@@ -0,0 +1,11 @@
1
+ export {
2
+ getTranslation,
3
+ LocaleClientContextProvider,
4
+ LocaleClientContext,
5
+ useIntlayer,
6
+ useTraduction,
7
+ useLocaleCookie,
8
+ } from 'react-intlayer';
9
+ export { generateStaticParams } from './generateStaticParams';
10
+ export type { LocalParams, NextPageIntlayer } from './types/index';
11
+ export { useLocale } from './client/useLocale';
@@ -0,0 +1 @@
1
+ export * from './intlayerMiddleware';
@@ -0,0 +1,215 @@
1
+ import { type Locales, intlayerConfiguration } from '@intlayer/config/client';
2
+ import { type NextRequest, NextResponse } from 'next/server';
3
+ import { localeDetector } from './localeDetector';
4
+
5
+ const { internationalization, middleware } = intlayerConfiguration;
6
+ const { locales, defaultLocale } = internationalization;
7
+ const {
8
+ headerName,
9
+ cookieName,
10
+ prefixDefault,
11
+ basePath,
12
+ serverSetCookie,
13
+ noPrefix,
14
+ } = middleware;
15
+
16
+ export const intlayerMiddleware = (request: NextRequest): NextResponse => {
17
+ const pathname = request.nextUrl.pathname;
18
+ const cookieLocale = getCookieLocale(request);
19
+ const basePathTrailingSlash = basePath.endsWith('/');
20
+
21
+ if (noPrefix) {
22
+ return handleNoPrefix(
23
+ request,
24
+ cookieLocale,
25
+ pathname,
26
+ basePathTrailingSlash
27
+ );
28
+ }
29
+
30
+ const pathLocale = getPathLocale(pathname);
31
+ return handlePrefix(
32
+ request,
33
+ cookieLocale,
34
+ pathLocale,
35
+ pathname,
36
+ basePathTrailingSlash
37
+ );
38
+ };
39
+
40
+ const getCookieLocale = (request: NextRequest): Locales | undefined => {
41
+ if (!cookieName) return undefined;
42
+ const cookieValue = request.cookies.get(cookieName)?.value as Locales;
43
+ if (cookieValue && locales.includes(cookieValue)) {
44
+ return cookieValue;
45
+ }
46
+ };
47
+
48
+ const handleNoPrefix = (
49
+ request: NextRequest,
50
+ cookieLocale: Locales | undefined,
51
+ pathname: string,
52
+ basePathTrailingSlash: boolean
53
+ ): NextResponse => {
54
+ const locale = cookieLocale ?? defaultLocale;
55
+ const newPath = constructPath(
56
+ locale,
57
+ pathname,
58
+ basePath,
59
+ basePathTrailingSlash
60
+ );
61
+ return rewriteUrl(request, newPath, locale);
62
+ };
63
+
64
+ const getPathLocale = (pathname: string): Locales | undefined =>
65
+ locales.find(
66
+ (locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`
67
+ );
68
+
69
+ const handlePrefix = (
70
+ request: NextRequest,
71
+ cookieLocale: Locales | undefined,
72
+ pathLocale: Locales | undefined,
73
+ pathname: string,
74
+ basePathTrailingSlash: boolean
75
+ ): NextResponse => {
76
+ if (!pathLocale) {
77
+ return handleMissingPathLocale(
78
+ request,
79
+ cookieLocale,
80
+ pathname,
81
+ basePathTrailingSlash
82
+ );
83
+ }
84
+ return handleExistingPathLocale(
85
+ request,
86
+ cookieLocale,
87
+ pathLocale,
88
+ pathname,
89
+ basePathTrailingSlash
90
+ );
91
+ };
92
+
93
+ const handleMissingPathLocale = (
94
+ request: NextRequest,
95
+ cookieLocale: Locales | undefined,
96
+ pathname: string,
97
+ basePathTrailingSlash: boolean
98
+ ): NextResponse => {
99
+ let locale = cookieLocale ?? localeDetector?.(request) ?? defaultLocale;
100
+ if (!locales.includes(locale)) {
101
+ console.warn(
102
+ 'The localeDetector callback must return a locale included in your locales array. Reverting to using defaultLocale.'
103
+ );
104
+ locale = defaultLocale;
105
+ }
106
+ const newPath = constructPath(
107
+ locale,
108
+ pathname,
109
+ basePath,
110
+ basePathTrailingSlash
111
+ );
112
+ return prefixDefault || locale !== defaultLocale
113
+ ? redirectUrl(request, newPath)
114
+ : rewriteUrl(request, newPath, locale);
115
+ };
116
+
117
+ const handleExistingPathLocale = (
118
+ request: NextRequest,
119
+ cookieLocale: Locales | undefined,
120
+ pathLocale: Locales,
121
+ pathname: string,
122
+ basePathTrailingSlash: boolean
123
+ ): NextResponse => {
124
+ if (
125
+ cookieLocale &&
126
+ cookieLocale !== pathLocale &&
127
+ serverSetCookie !== 'always'
128
+ ) {
129
+ const newPath = handleCookieLocaleMismatch(
130
+ request,
131
+ pathname,
132
+ pathLocale,
133
+ cookieLocale,
134
+ basePath,
135
+ basePathTrailingSlash
136
+ );
137
+ return redirectUrl(request, newPath);
138
+ }
139
+
140
+ return handleDefaultLocaleRedirect(
141
+ request,
142
+ pathLocale,
143
+ pathname,
144
+ basePathTrailingSlash
145
+ );
146
+ };
147
+
148
+ const handleCookieLocaleMismatch = (
149
+ request: NextRequest,
150
+
151
+ pathname: string,
152
+ pathLocale: Locales,
153
+ cookieLocale: Locales,
154
+ basePath: string,
155
+ basePathTrailingSlash: boolean
156
+ ): string => {
157
+ const newPath = pathname.replace(`/${pathLocale}`, `/${cookieLocale}`);
158
+ return constructPath(
159
+ cookieLocale,
160
+ newPath,
161
+ basePath,
162
+ basePathTrailingSlash,
163
+ request.nextUrl.search
164
+ );
165
+ };
166
+
167
+ const handleDefaultLocaleRedirect = (
168
+ request: NextRequest,
169
+ pathLocale: Locales,
170
+ pathname: string,
171
+ basePathTrailingSlash: boolean
172
+ ): NextResponse => {
173
+ if (!prefixDefault && pathLocale === defaultLocale) {
174
+ let pathWithoutLocale = pathname.slice(`/${pathLocale}`.length) || '/';
175
+
176
+ if (basePathTrailingSlash) {
177
+ pathWithoutLocale = pathWithoutLocale.slice(1);
178
+ }
179
+
180
+ if (request.nextUrl.search) {
181
+ pathWithoutLocale += request.nextUrl.search;
182
+ }
183
+
184
+ return rewriteUrl(request, `${basePath}${pathWithoutLocale}`, pathLocale);
185
+ }
186
+ return rewriteUrl(request, pathname, pathLocale);
187
+ };
188
+
189
+ const constructPath = (
190
+ locale: Locales,
191
+ path: string,
192
+ basePath: string,
193
+ basePathTrailingSlash: boolean,
194
+ search?: string
195
+ ): string => {
196
+ let newPath = `${locale}${path}`;
197
+ newPath = `${basePath}${basePathTrailingSlash ? '' : '/'}${newPath}`;
198
+ if (search) {
199
+ newPath += search;
200
+ }
201
+ return newPath;
202
+ };
203
+
204
+ const rewriteUrl = (
205
+ request: NextRequest,
206
+ newPath: string,
207
+ locale: Locales
208
+ ): NextResponse => {
209
+ const response = NextResponse.rewrite(new URL(newPath, request.url));
210
+ response.headers.set(headerName, locale);
211
+ return response;
212
+ };
213
+
214
+ const redirectUrl = (request: NextRequest, newPath: string): NextResponse =>
215
+ NextResponse.redirect(new URL(newPath, request.url));
@@ -0,0 +1,28 @@
1
+ import { match } from '@formatjs/intl-localematcher';
2
+ import type { Locales } from '@intlayer/config';
3
+ import { getConfiguration } from '@intlayer/config/client';
4
+ import Negotiator from 'negotiator';
5
+ import type { NextRequest } from 'next/server.js';
6
+
7
+ const { locales, defaultLocale } = getConfiguration().internationalization;
8
+
9
+ export const localeDetector = (request: NextRequest): Locales => {
10
+ const negotiatorHeaders: Record<string, string> = {};
11
+
12
+ request.headers.forEach((value, key) => (negotiatorHeaders[key] = value));
13
+
14
+ const languages = new Negotiator({ headers: negotiatorHeaders }).languages();
15
+
16
+ // match can only use specifically formatted locales
17
+ // https://stackoverflow.com/questions/76447732/nextjs-13-i18n-incorrect-locale-information-provided
18
+ try {
19
+ return match(languages, locales, defaultLocale) as Locales;
20
+ } catch (e) {
21
+ console.warn(
22
+ `No valid locales in accept-language header: ${languages.join(', ')}`
23
+ );
24
+ console.warn(`Reverting to using defaultLocale: ${defaultLocale}`);
25
+
26
+ return defaultLocale;
27
+ }
28
+ };
@@ -0,0 +1,9 @@
1
+ export {
2
+ getLocaleContent,
3
+ useTraduction,
4
+ LocaleServerContext,
5
+ LocaleServerContextProvider,
6
+ locale,
7
+ useIntlayer,
8
+ } from 'react-intlayer/server';
9
+ export { withIntlayer } from './withIntlayer';
@@ -0,0 +1,61 @@
1
+ import { resolve, relative, join } from 'path';
2
+ import { getConfiguration, formatEnvVariable } from '@intlayer/config';
3
+ import type { NextConfig } from 'next';
4
+ import type { NextJsWebpackConfig } from 'next/dist/server/config-shared';
5
+
6
+ type PluginOptions = {
7
+ // TODO: add options
8
+ };
9
+
10
+ const intlayerConfig = getConfiguration({
11
+ verbose: true,
12
+ });
13
+
14
+ // Set all configuration values as environment variables
15
+ const env = formatEnvVariable('NEXT_PUBLIC_INTLAYER_');
16
+ const { mainDir, baseDir } = intlayerConfig.content;
17
+
18
+ const mergeEnvVariable = (
19
+ nextEnv: Record<string, unknown> | undefined = {}
20
+ ): Record<string, string> => Object.assign({}, nextEnv, env);
21
+
22
+ const mergeStats = (
23
+ nextStats: Record<string, unknown> | undefined = {}
24
+ ): Record<string, unknown> =>
25
+ Object.assign({}, nextStats, {
26
+ warnings: false,
27
+ });
28
+
29
+ export const withIntlayer =
30
+ (_pluginOptions: PluginOptions = {}) =>
31
+ (nextConfig: Partial<NextConfig> = {}): Partial<NextConfig> => {
32
+ if (typeof nextConfig !== 'object') nextConfig = {};
33
+
34
+ return Object.assign({}, nextConfig, {
35
+ env: mergeEnvVariable(nextConfig.env),
36
+
37
+ stats: mergeStats(nextConfig.stats),
38
+
39
+ webpack: (config: Parameters<NextJsWebpackConfig>['0']) => {
40
+ const dictionariesPath = join(mainDir, 'dictionaries.cjs');
41
+ const relativeDictionariesPath = relative(baseDir, dictionariesPath);
42
+
43
+ config.externals.push({
44
+ esbuild: 'esbuild',
45
+ module: 'module',
46
+ fs: 'fs',
47
+ });
48
+
49
+ config.resolve.alias['@intlayer/dictionaries-entry'] = resolve(
50
+ relativeDictionariesPath
51
+ );
52
+
53
+ config.module.rules.push({
54
+ test: /\.node$/,
55
+ loader: 'node-loader',
56
+ });
57
+
58
+ return config;
59
+ },
60
+ });
61
+ };
@@ -0,0 +1,10 @@
1
+ import type { Locales } from 'intlayer';
2
+ import type { NextPage } from 'next';
3
+ import type { PropsWithChildren } from 'react';
4
+
5
+ export type LocalParams = {
6
+ params: { locale: Locales };
7
+ };
8
+
9
+ export type NextPageIntlayer = NextPage<LocalParams>;
10
+ export type NextLayoutIntlayer = NextPage<PropsWithChildren<LocalParams>>;
@@ -0,0 +1 @@
1
+ export type { LocalParams, NextPageIntlayer } from './NextPage';