next-intl 4.0.3 → 4.2.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/dist/esm/development/navigation/shared/createSharedNavigationFns.js +8 -7
- package/dist/esm/development/navigation/shared/utils.js +27 -1
- package/dist/esm/production/navigation/shared/createSharedNavigationFns.js +1 -1
- package/dist/esm/production/navigation/shared/utils.js +1 -1
- package/dist/types/navigation/react-client/createNavigation.d.ts +10 -4
- package/dist/types/navigation/react-server/createNavigation.d.ts +7 -4
- package/dist/types/navigation/shared/createSharedNavigationFns.d.ts +15 -6
- package/package.json +3 -3
|
@@ -40,8 +40,10 @@ function createSharedNavigationFns(getLocale, routing) {
|
|
|
40
40
|
href: pathnames == null ? pathname : {
|
|
41
41
|
pathname,
|
|
42
42
|
params
|
|
43
|
-
}
|
|
44
|
-
|
|
43
|
+
},
|
|
44
|
+
// Always include a prefix when changing locales
|
|
45
|
+
forcePrefix: locale != null || undefined
|
|
46
|
+
}) : pathname;
|
|
45
47
|
return /*#__PURE__*/jsx(BaseLink, {
|
|
46
48
|
ref: ref
|
|
47
49
|
// @ts-expect-error -- This is ok
|
|
@@ -56,9 +58,9 @@ function createSharedNavigationFns(getLocale, routing) {
|
|
|
56
58
|
});
|
|
57
59
|
}
|
|
58
60
|
const LinkWithRef = /*#__PURE__*/forwardRef(Link);
|
|
59
|
-
function getPathname(args
|
|
60
|
-
_forcePrefix) {
|
|
61
|
+
function getPathname(args) {
|
|
61
62
|
const {
|
|
63
|
+
forcePrefix,
|
|
62
64
|
href,
|
|
63
65
|
locale
|
|
64
66
|
} = args;
|
|
@@ -81,7 +83,7 @@ function createSharedNavigationFns(getLocale, routing) {
|
|
|
81
83
|
pathnames: config.pathnames
|
|
82
84
|
});
|
|
83
85
|
}
|
|
84
|
-
return applyPathnamePrefix(pathname, locale, config,
|
|
86
|
+
return applyPathnamePrefix(pathname, locale, config, forcePrefix);
|
|
85
87
|
}
|
|
86
88
|
function getRedirectFn(fn) {
|
|
87
89
|
/** @see https://next-intl.dev/docs/routing/navigation#redirect */
|
|
@@ -96,8 +98,7 @@ function createSharedNavigationFns(getLocale, routing) {
|
|
|
96
98
|
Link: LinkWithRef,
|
|
97
99
|
redirect: redirect$1,
|
|
98
100
|
permanentRedirect: permanentRedirect$1,
|
|
99
|
-
|
|
100
|
-
getPathname: getPathname
|
|
101
|
+
getPathname
|
|
101
102
|
};
|
|
102
103
|
}
|
|
103
104
|
|
|
@@ -62,12 +62,15 @@ function compileLocalizedPathname({
|
|
|
62
62
|
|
|
63
63
|
// Clean up optional catch-all segments that were not replaced
|
|
64
64
|
compiled = compiled.replace(/\[\[\.\.\..+\]\]/g, '');
|
|
65
|
-
compiled = normalizeTrailingSlash(compiled);
|
|
66
65
|
if (compiled.includes('[')) {
|
|
67
66
|
// Next.js throws anyway, therefore better provide a more helpful error message
|
|
68
67
|
throw new Error(`Insufficient params provided for localized pathname.\nTemplate: ${template}\nParams: ${JSON.stringify(params)}`);
|
|
69
68
|
}
|
|
69
|
+
compiled = normalizeTrailingSlash(compiled);
|
|
70
|
+
compiled = encodePathname(compiled);
|
|
70
71
|
if (query) {
|
|
72
|
+
// This also encodes non-ASCII characters by
|
|
73
|
+
// using `new URLSearchParams()` internally
|
|
71
74
|
compiled += serializeSearchParams(query);
|
|
72
75
|
}
|
|
73
76
|
return compiled;
|
|
@@ -90,6 +93,29 @@ function compileLocalizedPathname({
|
|
|
90
93
|
return result;
|
|
91
94
|
}
|
|
92
95
|
}
|
|
96
|
+
function encodePathname(pathname) {
|
|
97
|
+
// Generally, to comply with RFC 3986 and Google's best practices for URL structures
|
|
98
|
+
// (https://developers.google.com/search/docs/crawling-indexing/url-structure),
|
|
99
|
+
// we should always encode non-ASCII characters.
|
|
100
|
+
//
|
|
101
|
+
// There are two places where next-intl interacts with potentially non-ASCII URLs:
|
|
102
|
+
// 1. Middleware: When mapping a localized pathname to a non-localized pathname internally
|
|
103
|
+
// 2. Navigation APIs: When generating a URLs to be used for <Link /> & friends
|
|
104
|
+
//
|
|
105
|
+
// Next.js normalizes incoming pathnames to always be encoded, therefore we can safely
|
|
106
|
+
// decode them there (see middleware.tsx). On the other hand, Next.js doesn't consistently
|
|
107
|
+
// encode non-ASCII characters that are passed to navigation APIs:
|
|
108
|
+
// 1. <Link /> doesn't encode non-ASCII characters
|
|
109
|
+
// 2. useRouter() uses `new URL()` internally, which will encode—but only if necessary
|
|
110
|
+
// 3. redirect() uses useRouter() on the client, but on the server side only
|
|
111
|
+
// assigns the location header without encoding.
|
|
112
|
+
//
|
|
113
|
+
// In addition to this, for getPathname() we need to encode non-ASCII characters.
|
|
114
|
+
//
|
|
115
|
+
// Therefore, the bottom line is that next-intl should take care of encoding non-ASCII
|
|
116
|
+
// characters in all cases, but can rely on `new URL()` to not double-encode characters.
|
|
117
|
+
return pathname.split('/').map(segment => encodeURIComponent(segment)).join('/');
|
|
118
|
+
}
|
|
93
119
|
function getRoute(locale, pathname, pathnames) {
|
|
94
120
|
const sortedPathnames = getSortedPathnames(Object.keys(pathnames));
|
|
95
121
|
const decoded = decodeURI(pathname);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{redirect as e,permanentRedirect as t}from"next/navigation";import{forwardRef as o}from"react";import{receiveRoutingConfig as r}from"../../routing/config.js";import n from"../../shared/use.js";import{isLocalizableHref as a,isPromise as i}from"../../shared/utils.js";import
|
|
1
|
+
import{redirect as e,permanentRedirect as t}from"next/navigation";import{forwardRef as o}from"react";import{receiveRoutingConfig as r}from"../../routing/config.js";import n from"../../shared/use.js";import{isLocalizableHref as a,isPromise as i}from"../../shared/utils.js";import c from"./BaseLink.js";import{serializeSearchParams as f,compileLocalizedPathname as m,applyPathnamePrefix as l,normalizeNameOrNameWithParams as s}from"./utils.js";import{jsx as p}from"react/jsx-runtime";function u(u,h){const j=r(h||{}),d=j.pathnames;function g({href:e,locale:t,...o},r){let f,m;"object"==typeof e?(f=e.pathname,m=e.params):f=e;const l=a(e),s=u(),h=i(s)?n(s):s,g=l?y({locale:t||h,href:null==d?f:{pathname:f,params:m},forcePrefix:null!=t||void 0}):f;return p(c,{ref:r,href:"object"==typeof e?{...e,pathname:g}:g,locale:t,localeCookie:j.localeCookie,...o})}const x=o(g);function y(e){const{forcePrefix:t,href:o,locale:r}=e;let n;return null==d?"object"==typeof o?(n=o.pathname,o.query&&(n+=f(o.query))):n=o:n=m({locale:r,...s(o),pathnames:j.pathnames}),l(n,r,j,t)}function k(e){return function(t,...o){return e(y(t),...o)}}const b=k(e),P=k(t);return{config:j,Link:x,redirect:b,permanentRedirect:P,getPathname:y}}export{u as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{getSortedPathnames as e,matchesPathname as n,isLocalizableHref as t,prefixPathname as r,getLocalizedTemplate as o,normalizeTrailingSlash as a,getLocalePrefix as i}from"../../shared/utils.js";function c(e){return"string"==typeof e?{pathname:e}:e}function s(e){function n(e){return String(e)}const t=new URLSearchParams;for(const[r,o]of Object.entries(e))Array.isArray(o)?o.forEach((e=>{t.append(r,n(e))})):t.set(r,n(o));return"?"+t.toString()}function f({pathname:e,locale:n,params:t,pathnames:r,query:i}){function c(e){let n=r[e];return n||(n=e),n}function f(e,r){let c=o(e,n,r);return t&&Object.entries(t).forEach((([e,n])=>{let t,r;Array.isArray(n)?(t=`(\\[)?\\[...${e}\\](\\])?`,r=n.map((e=>String(e))).join("/")):(t=`\\[${e}\\]`,r=String(n)),c=c.replace(new RegExp(t,"g"),r)})),c=c.replace(/\[\[\.\.\..+\]\]/g,""),c=a(c),i&&(c+=s(i)),c}if("string"==typeof e){return f(c(e),e)}{const{pathname:n,...t}=e;return{...t,pathname:f(c(n),n)}}}function u(t,r,a){const i=e(Object.keys(a)),c=decodeURI(r);for(const e of i){const r=a[e];if("string"==typeof r){if(n(r,c))return e}else if(n(o(r,t,e),c))return e}return r}function l(e,n=window.location.pathname){return"/"===e?n:n.replace(e,"")}function p(e,n,o,a){const{mode:c}=o.localePrefix;let s;return void 0!==a?s=a:t(e)&&("always"===c?s=!0:"as-needed"===c&&(s=o.domains?!o.domains.some((e=>e.defaultLocale===n)):n!==o.defaultLocale)),s?r(i(n,o.localePrefix),e):e}export{p as applyPathnamePrefix,f as compileLocalizedPathname,l as getBasePath,u as getRoute,c as normalizeNameOrNameWithParams,s as serializeSearchParams};
|
|
1
|
+
import{getSortedPathnames as e,matchesPathname as n,isLocalizableHref as t,prefixPathname as r,getLocalizedTemplate as o,normalizeTrailingSlash as a,getLocalePrefix as i}from"../../shared/utils.js";function c(e){return"string"==typeof e?{pathname:e}:e}function s(e){function n(e){return String(e)}const t=new URLSearchParams;for(const[r,o]of Object.entries(e))Array.isArray(o)?o.forEach((e=>{t.append(r,n(e))})):t.set(r,n(o));return"?"+t.toString()}function f({pathname:e,locale:n,params:t,pathnames:r,query:i}){function c(e){let n=r[e];return n||(n=e),n}function f(e,r){let c=o(e,n,r);return t&&Object.entries(t).forEach((([e,n])=>{let t,r;Array.isArray(n)?(t=`(\\[)?\\[...${e}\\](\\])?`,r=n.map((e=>String(e))).join("/")):(t=`\\[${e}\\]`,r=String(n)),c=c.replace(new RegExp(t,"g"),r)})),c=c.replace(/\[\[\.\.\..+\]\]/g,""),c=a(c),c=function(e){return e.split("/").map((e=>encodeURIComponent(e))).join("/")}(c),i&&(c+=s(i)),c}if("string"==typeof e){return f(c(e),e)}{const{pathname:n,...t}=e;return{...t,pathname:f(c(n),n)}}}function u(t,r,a){const i=e(Object.keys(a)),c=decodeURI(r);for(const e of i){const r=a[e];if("string"==typeof r){if(n(r,c))return e}else if(n(o(r,t,e),c))return e}return r}function l(e,n=window.location.pathname){return"/"===e?n:n.replace(e,"")}function p(e,n,o,a){const{mode:c}=o.localePrefix;let s;return void 0!==a?s=a:t(e)&&("always"===c?s=!0:"as-needed"===c&&(s=o.domains?!o.domains.some((e=>e.defaultLocale===n)):n!==o.defaultLocale)),s?r(i(n,o.localePrefix),e):e}export{p as applyPathnamePrefix,f as compileLocalizedPathname,l as getBasePath,u as getRoute,c as normalizeNameOrNameWithParams,s as serializeSearchParams};
|
|
@@ -335,6 +335,7 @@ export default function createNavigation<const AppLocales extends Locales, const
|
|
|
335
335
|
query?: import("../shared/utils.js").QueryParams;
|
|
336
336
|
}) : never : never;
|
|
337
337
|
locale: Locale;
|
|
338
|
+
forcePrefix?: boolean;
|
|
338
339
|
}) => string>[0]["href"], options?: (Partial<import("next/dist/shared/lib/app-router-context.shared-runtime.js").NavigateOptions> & {
|
|
339
340
|
locale?: Locale;
|
|
340
341
|
}) | undefined) => void;
|
|
@@ -359,6 +360,7 @@ export default function createNavigation<const AppLocales extends Locales, const
|
|
|
359
360
|
query?: import("../shared/utils.js").QueryParams;
|
|
360
361
|
}) : never : never;
|
|
361
362
|
locale: Locale;
|
|
363
|
+
forcePrefix?: boolean;
|
|
362
364
|
}) => string>[0]["href"], options?: (Partial<import("next/dist/shared/lib/app-router-context.shared-runtime.js").NavigateOptions> & {
|
|
363
365
|
locale?: Locale;
|
|
364
366
|
}) | undefined) => void;
|
|
@@ -383,6 +385,7 @@ export default function createNavigation<const AppLocales extends Locales, const
|
|
|
383
385
|
query?: import("../shared/utils.js").QueryParams;
|
|
384
386
|
}) : never : never;
|
|
385
387
|
locale: Locale;
|
|
388
|
+
forcePrefix?: boolean;
|
|
386
389
|
}) => string>[0]["href"], options?: (Partial<import("next/dist/shared/lib/app-router-context.shared-runtime.js").PrefetchOptions> & {
|
|
387
390
|
locale?: Locale;
|
|
388
391
|
}) | undefined) => void;
|
|
@@ -410,8 +413,9 @@ export default function createNavigation<const AppLocales extends Locales, const
|
|
|
410
413
|
query?: import("../shared/utils.js").QueryParams;
|
|
411
414
|
}) : never : never;
|
|
412
415
|
locale: Locale;
|
|
416
|
+
forcePrefix?: boolean;
|
|
413
417
|
}) => string;
|
|
414
|
-
redirect: (args:
|
|
418
|
+
redirect: (args: {
|
|
415
419
|
href: [AppPathnames] extends [never] ? string | {
|
|
416
420
|
pathname: string;
|
|
417
421
|
query?: import("../shared/utils.js").QueryParams;
|
|
@@ -431,8 +435,9 @@ export default function createNavigation<const AppLocales extends Locales, const
|
|
|
431
435
|
query?: import("../shared/utils.js").QueryParams;
|
|
432
436
|
}) : never : never;
|
|
433
437
|
locale: Locale;
|
|
434
|
-
|
|
435
|
-
|
|
438
|
+
forcePrefix?: boolean;
|
|
439
|
+
}, type?: import("next/navigation.js").RedirectType | undefined) => never;
|
|
440
|
+
permanentRedirect: (args: {
|
|
436
441
|
href: [AppPathnames] extends [never] ? string | {
|
|
437
442
|
pathname: string;
|
|
438
443
|
query?: import("../shared/utils.js").QueryParams;
|
|
@@ -452,5 +457,6 @@ export default function createNavigation<const AppLocales extends Locales, const
|
|
|
452
457
|
query?: import("../shared/utils.js").QueryParams;
|
|
453
458
|
}) : never : never;
|
|
454
459
|
locale: Locale;
|
|
455
|
-
|
|
460
|
+
forcePrefix?: boolean;
|
|
461
|
+
}, type?: import("next/navigation.js").RedirectType | undefined) => never;
|
|
456
462
|
};
|
|
@@ -313,7 +313,7 @@ export default function createNavigation<const AppLocales extends Locales, const
|
|
|
313
313
|
pathname: T;
|
|
314
314
|
} & Omit<import("url").UrlObject, "pathname">) : never : never;
|
|
315
315
|
}, "ref"> & import("react").RefAttributes<HTMLAnchorElement>>;
|
|
316
|
-
redirect: (args:
|
|
316
|
+
redirect: (args: {
|
|
317
317
|
href: [AppPathnames] extends [never] ? string | {
|
|
318
318
|
pathname: string;
|
|
319
319
|
query?: import("../shared/utils.js").QueryParams;
|
|
@@ -333,8 +333,9 @@ export default function createNavigation<const AppLocales extends Locales, const
|
|
|
333
333
|
query?: import("../shared/utils.js").QueryParams;
|
|
334
334
|
}) : never : never;
|
|
335
335
|
locale: import("use-intl").Locale;
|
|
336
|
-
|
|
337
|
-
|
|
336
|
+
forcePrefix?: boolean;
|
|
337
|
+
}, type?: import("next/navigation.js").RedirectType | undefined) => never;
|
|
338
|
+
permanentRedirect: (args: {
|
|
338
339
|
href: [AppPathnames] extends [never] ? string | {
|
|
339
340
|
pathname: string;
|
|
340
341
|
query?: import("../shared/utils.js").QueryParams;
|
|
@@ -354,7 +355,8 @@ export default function createNavigation<const AppLocales extends Locales, const
|
|
|
354
355
|
query?: import("../shared/utils.js").QueryParams;
|
|
355
356
|
}) : never : never;
|
|
356
357
|
locale: import("use-intl").Locale;
|
|
357
|
-
|
|
358
|
+
forcePrefix?: boolean;
|
|
359
|
+
}, type?: import("next/navigation.js").RedirectType | undefined) => never;
|
|
358
360
|
getPathname: (args: {
|
|
359
361
|
href: [AppPathnames] extends [never] ? string | {
|
|
360
362
|
pathname: string;
|
|
@@ -375,5 +377,6 @@ export default function createNavigation<const AppLocales extends Locales, const
|
|
|
375
377
|
query?: import("../shared/utils.js").QueryParams;
|
|
376
378
|
}) : never : never;
|
|
377
379
|
locale: import("use-intl").Locale;
|
|
380
|
+
forcePrefix?: boolean;
|
|
378
381
|
}) => string;
|
|
379
382
|
};
|
|
@@ -331,29 +331,38 @@ export default function createSharedNavigationFns<const AppLocales extends Local
|
|
|
331
331
|
pathname: T;
|
|
332
332
|
} & Omit<import("url").UrlObject, "pathname">) : never : never;
|
|
333
333
|
}, "ref"> & import("react").RefAttributes<HTMLAnchorElement>>;
|
|
334
|
-
redirect: (args:
|
|
334
|
+
redirect: (args: Parameters<(args: {
|
|
335
335
|
/** @see https://next-intl.dev/docs/routing/navigation#getpathname */
|
|
336
336
|
href: [AppPathnames] extends [never] ? string | {
|
|
337
337
|
pathname: string;
|
|
338
338
|
query?: QueryParams;
|
|
339
339
|
} : HrefOrHrefWithParams<keyof AppPathnames>;
|
|
340
|
+
/** The locale to compute the pathname for. */
|
|
340
341
|
locale: Locale;
|
|
341
|
-
|
|
342
|
-
|
|
342
|
+
/** Will prepend the pathname with the locale prefix, regardless of your `localePrefix` setting. This can be helpful to update a locale cookie when changing locales. */
|
|
343
|
+
forcePrefix?: boolean;
|
|
344
|
+
}) => string>[0], type?: import("next/navigation.js").RedirectType | undefined) => never;
|
|
345
|
+
permanentRedirect: (args: Parameters<(args: {
|
|
343
346
|
/** @see https://next-intl.dev/docs/routing/navigation#getpathname */
|
|
344
347
|
href: [AppPathnames] extends [never] ? string | {
|
|
345
348
|
pathname: string;
|
|
346
349
|
query?: QueryParams;
|
|
347
350
|
} : HrefOrHrefWithParams<keyof AppPathnames>;
|
|
351
|
+
/** The locale to compute the pathname for. */
|
|
348
352
|
locale: Locale;
|
|
349
|
-
|
|
350
|
-
|
|
353
|
+
/** Will prepend the pathname with the locale prefix, regardless of your `localePrefix` setting. This can be helpful to update a locale cookie when changing locales. */
|
|
354
|
+
forcePrefix?: boolean;
|
|
355
|
+
}) => string>[0], type?: import("next/navigation.js").RedirectType | undefined) => never;
|
|
356
|
+
getPathname: (args: {
|
|
351
357
|
/** @see https://next-intl.dev/docs/routing/navigation#getpathname */
|
|
352
358
|
href: [AppPathnames] extends [never] ? string | {
|
|
353
359
|
pathname: string;
|
|
354
360
|
query?: QueryParams;
|
|
355
361
|
} : HrefOrHrefWithParams<keyof AppPathnames>;
|
|
362
|
+
/** The locale to compute the pathname for. */
|
|
356
363
|
locale: Locale;
|
|
357
|
-
|
|
364
|
+
/** Will prepend the pathname with the locale prefix, regardless of your `localePrefix` setting. This can be helpful to update a locale cookie when changing locales. */
|
|
365
|
+
forcePrefix?: boolean;
|
|
366
|
+
}) => string;
|
|
358
367
|
};
|
|
359
368
|
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "next-intl",
|
|
3
|
-
"version": "4.0
|
|
3
|
+
"version": "4.2.0",
|
|
4
4
|
"sideEffects": false,
|
|
5
5
|
"author": "Jan Amann <jan@amann.work>",
|
|
6
6
|
"funding": [
|
|
@@ -112,7 +112,7 @@
|
|
|
112
112
|
"dependencies": {
|
|
113
113
|
"@formatjs/intl-localematcher": "^0.5.4",
|
|
114
114
|
"negotiator": "^1.0.0",
|
|
115
|
-
"use-intl": "^4.0
|
|
115
|
+
"use-intl": "^4.2.0"
|
|
116
116
|
},
|
|
117
117
|
"peerDependencies": {
|
|
118
118
|
"next": "^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0",
|
|
@@ -124,5 +124,5 @@
|
|
|
124
124
|
"optional": true
|
|
125
125
|
}
|
|
126
126
|
},
|
|
127
|
-
"gitHead": "
|
|
127
|
+
"gitHead": "e303923f7339356cf2c1d64adcdc9498531d8db8"
|
|
128
128
|
}
|