next-intl 4.1.0 → 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.
|
@@ -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{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};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "next-intl",
|
|
3
|
-
"version": "4.
|
|
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.
|
|
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
|
}
|