next-intl 4.0.0-beta-021e874 → 4.0.0-beta-dea867b

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.
@@ -5,6 +5,7 @@ import { getHost, getNormalizedPathname, getLocalePrefixes, isLocaleSupportedOnD
5
5
  * See https://developers.google.com/search/docs/specialty/international/localized-versions
6
6
  */
7
7
  function getAlternateLinksHeaderValue({
8
+ internalTemplateName,
8
9
  localizedPathnames,
9
10
  request,
10
11
  resolvedLocale,
@@ -28,7 +29,8 @@ function getAlternateLinksHeaderValue({
28
29
  }
29
30
  function getLocalizedPathname(pathname, locale) {
30
31
  if (localizedPathnames && typeof localizedPathnames === 'object') {
31
- return formatTemplatePathname(pathname, localizedPathnames[resolvedLocale], localizedPathnames[locale]);
32
+ const sourceTemplate = localizedPathnames[resolvedLocale];
33
+ return formatTemplatePathname(pathname, sourceTemplate ?? internalTemplateName, localizedPathnames[locale] ?? internalTemplateName);
32
34
  } else {
33
35
  return pathname;
34
36
  }
@@ -77,8 +79,11 @@ function getAlternateLinksHeaderValue({
77
79
  // For domain-based routing there is no reasonable x-default
78
80
  !routing.domains || routing.domains.length === 0;
79
81
  if (shouldAddXDefault) {
80
- const url = new URL(getLocalizedPathname(normalizedUrl.pathname, routing.defaultLocale), normalizedUrl);
81
- links.push(getAlternateEntry(url, 'x-default'));
82
+ const localizedPathname = getLocalizedPathname(normalizedUrl.pathname, routing.defaultLocale);
83
+ if (localizedPathname) {
84
+ const url = new URL(localizedPathname, normalizedUrl);
85
+ links.push(getAlternateEntry(url, 'x-default'));
86
+ }
82
87
  }
83
88
  return links.join(', ');
84
89
  }
@@ -1,7 +1,7 @@
1
1
  import { NextResponse } from 'next/server';
2
2
  import { receiveRoutingConfig } from '../routing/config.js';
3
3
  import { HEADER_LOCALE_NAME } from '../shared/constants.js';
4
- import { matchesPathname, normalizeTrailingSlash, getLocalePrefix } from '../shared/utils.js';
4
+ import { matchesPathname, normalizeTrailingSlash, getLocalePrefix, getLocalizedTemplate } from '../shared/utils.js';
5
5
  import getAlternateLinksHeaderValue from './getAlternateLinksHeaderValue.js';
6
6
  import resolveLocale from './resolveLocale.js';
7
7
  import syncCookie from './syncCookie.js';
@@ -83,14 +83,14 @@ function createMiddleware(routing) {
83
83
  [resolvedTemplateLocale, internalTemplateName] = getInternalTemplate(pathnames, unprefixedExternalPathname, locale);
84
84
  if (internalTemplateName) {
85
85
  const pathnameConfig = pathnames[internalTemplateName];
86
- const localeTemplate = typeof pathnameConfig === 'string' ? pathnameConfig : pathnameConfig[locale];
86
+ const localeTemplate = getLocalizedTemplate(pathnameConfig, locale, internalTemplateName);
87
87
  if (matchesPathname(localeTemplate, unprefixedExternalPathname)) {
88
88
  unprefixedInternalPathname = formatTemplatePathname(unprefixedExternalPathname, localeTemplate, internalTemplateName);
89
89
  } else {
90
90
  let sourceTemplate;
91
91
  if (resolvedTemplateLocale) {
92
92
  // A localized pathname from another locale has matched
93
- sourceTemplate = typeof pathnameConfig === 'string' ? pathnameConfig : pathnameConfig[resolvedTemplateLocale];
93
+ sourceTemplate = getLocalizedTemplate(pathnameConfig, resolvedTemplateLocale, internalTemplateName);
94
94
  } else {
95
95
  // An internal pathname has matched that
96
96
  // doesn't have a localized pathname
@@ -146,6 +146,7 @@ function createMiddleware(routing) {
146
146
  if (!hasRedirected && resolvedRouting.localePrefix.mode !== 'never' && resolvedRouting.alternateLinks && resolvedRouting.locales.length > 1) {
147
147
  response.headers.set('Link', getAlternateLinksHeaderValue({
148
148
  routing: resolvedRouting,
149
+ internalTemplateName,
149
150
  localizedPathnames: internalTemplateName != null && pathnames ? pathnames[internalTemplateName] : undefined,
150
151
  request,
151
152
  resolvedLocale: locale
@@ -1,4 +1,4 @@
1
- import { getSortedPathnames, matchesPathname, normalizeTrailingSlash, getLocalePrefix, templateToRegex, prefixPathname } from '../shared/utils.js';
1
+ import { getSortedPathnames, matchesPathname, normalizeTrailingSlash, getLocalePrefix, templateToRegex, prefixPathname, getLocalizedTemplate } from '../shared/utils.js';
2
2
 
3
3
  function getInternalTemplate(pathnames, pathname, locale) {
4
4
  const sortedPathnames = getSortedPathnames(Object.keys(pathnames));
@@ -19,8 +19,9 @@ function getInternalTemplate(pathnames, pathname, locale) {
19
19
  if (curLocaleIndex > 0) {
20
20
  sortedEntries.unshift(sortedEntries.splice(curLocaleIndex, 1)[0]);
21
21
  }
22
- for (const [entryLocale, entryPathname] of sortedEntries) {
23
- if (matchesPathname(entryPathname, pathname)) {
22
+ for (const [entryLocale] of sortedEntries) {
23
+ const localizedTemplate = getLocalizedTemplate(pathnames[internalPathname], entryLocale, internalPathname);
24
+ if (matchesPathname(localizedTemplate, pathname)) {
24
25
  return [entryLocale, internalPathname];
25
26
  }
26
27
  }
@@ -1,4 +1,4 @@
1
- import { getSortedPathnames, matchesPathname, isLocalizableHref, prefixPathname, getLocalePrefix, normalizeTrailingSlash } from '../../shared/utils.js';
1
+ import { getSortedPathnames, matchesPathname, getLocalizedTemplate, isLocalizableHref, prefixPathname, getLocalePrefix, normalizeTrailingSlash } from '../../shared/utils.js';
2
2
 
3
3
  // Minor false positive: A route that has both optional and
4
4
  // required params will allow optional params.
@@ -43,8 +43,8 @@ function compileLocalizedPathname({
43
43
  }
44
44
  return namedPath;
45
45
  }
46
- function compilePath(namedPath) {
47
- const template = typeof namedPath === 'string' ? namedPath : namedPath[locale];
46
+ function compilePath(namedPath, internalPathname) {
47
+ const template = getLocalizedTemplate(namedPath, locale, internalPathname);
48
48
  let compiled = template;
49
49
  if (params) {
50
50
  Object.entries(params).forEach(([key, value]) => {
@@ -74,15 +74,15 @@ function compileLocalizedPathname({
74
74
  }
75
75
  if (typeof pathname === 'string') {
76
76
  const namedPath = getNamedPath(pathname);
77
- const compiled = compilePath(namedPath);
77
+ const compiled = compilePath(namedPath, pathname);
78
78
  return compiled;
79
79
  } else {
80
80
  const {
81
- pathname: href,
81
+ pathname: internalPathname,
82
82
  ...rest
83
83
  } = pathname;
84
- const namedPath = getNamedPath(href);
85
- const compiled = compilePath(namedPath);
84
+ const namedPath = getNamedPath(internalPathname);
85
+ const compiled = compilePath(namedPath, internalPathname);
86
86
  const result = {
87
87
  ...rest,
88
88
  pathname: compiled
@@ -101,7 +101,7 @@ function getRoute(locale, pathname, pathnames) {
101
101
  return internalPathname;
102
102
  }
103
103
  } else {
104
- if (matchesPathname(localizedPathnamesOrPathname[locale], decoded)) {
104
+ if (matchesPathname(getLocalizedTemplate(localizedPathnamesOrPathname, locale, internalPathname), decoded)) {
105
105
  return internalPathname;
106
106
  }
107
107
  }
@@ -37,6 +37,9 @@ function hasTrailingSlash() {
37
37
  return false;
38
38
  }
39
39
  }
40
+ function getLocalizedTemplate(pathnameConfig, locale, internalTemplate) {
41
+ return typeof pathnameConfig === 'string' ? pathnameConfig : pathnameConfig[locale] || internalTemplate;
42
+ }
40
43
  function normalizeTrailingSlash(pathname) {
41
44
  const trailingSlash = hasTrailingSlash();
42
45
  if (pathname !== '/') {
@@ -127,4 +130,4 @@ function isPromise(value) {
127
130
  return typeof value.then === 'function';
128
131
  }
129
132
 
130
- export { getLocaleAsPrefix, getLocalePrefix, getSortedPathnames, hasPathnamePrefixed, isLocalizableHref, isPromise, matchesPathname, normalizeTrailingSlash, prefixPathname, templateToRegex, unprefixPathname };
133
+ export { getLocaleAsPrefix, getLocalePrefix, getLocalizedTemplate, getSortedPathnames, hasPathnamePrefixed, isLocalizableHref, isPromise, matchesPathname, normalizeTrailingSlash, prefixPathname, templateToRegex, unprefixPathname };
@@ -1 +1 @@
1
- import{normalizeTrailingSlash as e}from"../shared/utils.js";import{getHost as a,getNormalizedPathname as t,getLocalePrefixes as o,isLocaleSupportedOnDomain as n,applyBasePath as l,formatTemplatePathname as r}from"./utils.js";function s({localizedPathnames:s,request:m,resolvedLocale:p,routing:h}){const i=m.nextUrl.clone(),c=a(m.headers);function f(a,t){return a.pathname=e(a.pathname),m.nextUrl.basePath&&((a=new URL(a)).pathname=l(a.pathname,m.nextUrl.basePath)),`<${a.toString()}>; rel="alternate"; hreflang="${t}"`}function u(e,a){return s&&"object"==typeof s?r(e,s[p],s[a]):e}c&&(i.port="",i.host=c),i.protocol=m.headers.get("x-forwarded-proto")??i.protocol,i.pathname=t(i.pathname,h.locales,h.localePrefix);const d=o(h.locales,h.localePrefix,!1).flatMap((([e,a])=>{function t(e){return"/"===e?a:a+e}let o;if(h.domains){return h.domains.filter((a=>n(e,a))).map((a=>(o=new URL(i),o.port="",o.host=a.domain,o.pathname=u(i.pathname,e),e===a.defaultLocale&&"always"!==h.localePrefix.mode||(o.pathname=t(o.pathname)),f(o,e))))}{let a;a=s&&"object"==typeof s?u(i.pathname,e):i.pathname,e===h.defaultLocale&&"always"!==h.localePrefix.mode||(a=t(a)),o=new URL(a,i)}return f(o,e)}));if(!h.domains||0===h.domains.length){const e=new URL(u(i.pathname,h.defaultLocale),i);d.push(f(e,"x-default"))}return d.join(", ")}export{s as default};
1
+ import{normalizeTrailingSlash as e}from"../shared/utils.js";import{getHost as a,getNormalizedPathname as t,getLocalePrefixes as n,isLocaleSupportedOnDomain as o,applyBasePath as r,formatTemplatePathname as l}from"./utils.js";function s({internalTemplateName:s,localizedPathnames:m,request:i,resolvedLocale:p,routing:c}){const f=i.nextUrl.clone(),h=a(i.headers);function u(a,t){return a.pathname=e(a.pathname),i.nextUrl.basePath&&((a=new URL(a)).pathname=r(a.pathname,i.nextUrl.basePath)),`<${a.toString()}>; rel="alternate"; hreflang="${t}"`}function d(e,a){if(m&&"object"==typeof m){const t=m[p];return l(e,t??s,m[a]??s)}return e}h&&(f.port="",f.host=h),f.protocol=i.headers.get("x-forwarded-proto")??f.protocol,f.pathname=t(f.pathname,c.locales,c.localePrefix);const x=n(c.locales,c.localePrefix,!1).flatMap((([e,a])=>{function t(e){return"/"===e?a:a+e}let n;if(c.domains){return c.domains.filter((a=>o(e,a))).map((a=>(n=new URL(f),n.port="",n.host=a.domain,n.pathname=d(f.pathname,e),e===a.defaultLocale&&"always"!==c.localePrefix.mode||(n.pathname=t(n.pathname)),u(n,e))))}{let a;a=m&&"object"==typeof m?d(f.pathname,e):f.pathname,e===c.defaultLocale&&"always"!==c.localePrefix.mode||(a=t(a)),n=new URL(a,f)}return u(n,e)}));if(!c.domains||0===c.domains.length){const e=d(f.pathname,c.defaultLocale);if(e){const a=new URL(e,f);x.push(u(a,"x-default"))}}return x.join(", ")}export{s as default};
@@ -1 +1 @@
1
- import{NextResponse as e}from"next/server";import{receiveRoutingConfig as t}from"../routing/config.js";import{HEADER_LOCALE_NAME as r}from"../shared/constants.js";import{matchesPathname as o,normalizeTrailingSlash as a,getLocalePrefix as l}from"../shared/utils.js";import n from"./getAlternateLinksHeaderValue.js";import s from"./resolveLocale.js";import i from"./syncCookie.js";import{sanitizePathname as c,isLocaleSupportedOnDomain as d,getNormalizedPathname as f,getPathnameMatch as m,getInternalTemplate as h,formatTemplatePathname as x,formatPathname as p,getBestMatchingDomain as u,applyBasePath as U,getLocaleAsPrefix as P}from"./utils.js";function g(g){const v=t(g);return function(t){let g;try{g=decodeURI(t.nextUrl.pathname)}catch{return e.next()}const L=c(g),{domain:j,locale:w}=s(v,t.headers,t.cookies,L),k=j?j.defaultLocale===w:w===v.defaultLocale,b=v.domains?.filter((e=>d(w,e)))||[],y=null!=v.domains&&!j;function R(o){const a=new URL(o,t.url);t.nextUrl.basePath&&(a.pathname=U(a.pathname,t.nextUrl.basePath));const l=new Headers(t.headers);return l.set(r,w),e.rewrite(a,{request:{headers:l}})}function q(r,o){const l=new URL(r,t.url);if(l.pathname=a(l.pathname),b.length>0&&!o&&j){const e=u(j,w,b);e&&(o=e.domain,e.defaultLocale===w&&"as-needed"===v.localePrefix.mode&&(l.pathname=f(l.pathname,v.locales,v.localePrefix)))}if(o&&(l.host=o,t.headers.get("x-forwarded-host"))){l.protocol=t.headers.get("x-forwarded-proto")??t.nextUrl.protocol;const e=o.split(":")[1];l.port=e??t.headers.get("x-forwarded-port")??""}return t.nextUrl.basePath&&(l.pathname=U(l.pathname,t.nextUrl.basePath)),V=!0,e.redirect(l.toString())}const H=f(L,v.locales,v.localePrefix),z=m(L,v.locales,v.localePrefix,j),A=null!=z,C="never"===v.localePrefix.mode||k&&"as-needed"===v.localePrefix.mode;let I,S,V,B=H;const D=v.pathnames;if(D){let e;if([e,S]=h(D,H,w),S){const r=D[S],a="string"==typeof r?r:r[w];if(o(a,H))B=x(H,a,S);else{let o;o=e?"string"==typeof r?r:r[e]:S;const n=C?void 0:l(w,v.localePrefix),s=x(H,o,a);I=q(p(s,n,t.nextUrl.search))}}}if(!I)if("/"!==B||A){const e=p(B,P(w),t.nextUrl.search);if(A){const r=p(H,z.prefix,t.nextUrl.search);if("never"===v.localePrefix.mode)I=q(p(H,void 0,t.nextUrl.search));else if(z.exact)if(k&&C)I=q(p(H,void 0,t.nextUrl.search));else if(v.domains){const t=u(j,z.locale,b);I=j?.domain===t?.domain||y?R(e):q(r,t?.domain)}else I=R(e);else I=q(r)}else I=C?R(e):q(p(H,l(w,v.localePrefix),t.nextUrl.search))}else I=C?R(p(B,P(w),t.nextUrl.search)):q(p(H,l(w,v.localePrefix),t.nextUrl.search));return i(t,I,w,v,j),!V&&"never"!==v.localePrefix.mode&&v.alternateLinks&&v.locales.length>1&&I.headers.set("Link",n({routing:v,localizedPathnames:null!=S&&D?D[S]:void 0,request:t,resolvedLocale:w})),I}}export{g as default};
1
+ import{NextResponse as e}from"next/server";import{receiveRoutingConfig as t}from"../routing/config.js";import{HEADER_LOCALE_NAME as r}from"../shared/constants.js";import{matchesPathname as o,normalizeTrailingSlash as a,getLocalePrefix as l,getLocalizedTemplate as n}from"../shared/utils.js";import s from"./getAlternateLinksHeaderValue.js";import i from"./resolveLocale.js";import c from"./syncCookie.js";import{sanitizePathname as d,isLocaleSupportedOnDomain as f,getNormalizedPathname as m,getPathnameMatch as h,getInternalTemplate as x,formatTemplatePathname as p,formatPathname as u,getBestMatchingDomain as U,applyBasePath as P,getLocaleAsPrefix as g}from"./utils.js";function v(v){const L=t(v);return function(t){let v;try{v=decodeURI(t.nextUrl.pathname)}catch{return e.next()}const j=d(v),{domain:w,locale:k}=i(L,t.headers,t.cookies,j),b=w?w.defaultLocale===k:k===L.defaultLocale,R=L.domains?.filter((e=>f(k,e)))||[],q=null!=L.domains&&!w;function y(o){const a=new URL(o,t.url);t.nextUrl.basePath&&(a.pathname=P(a.pathname,t.nextUrl.basePath));const l=new Headers(t.headers);return l.set(r,k),e.rewrite(a,{request:{headers:l}})}function H(r,o){const l=new URL(r,t.url);if(l.pathname=a(l.pathname),R.length>0&&!o&&w){const e=U(w,k,R);e&&(o=e.domain,e.defaultLocale===k&&"as-needed"===L.localePrefix.mode&&(l.pathname=m(l.pathname,L.locales,L.localePrefix)))}if(o&&(l.host=o,t.headers.get("x-forwarded-host"))){l.protocol=t.headers.get("x-forwarded-proto")??t.nextUrl.protocol;const e=o.split(":")[1];l.port=e??t.headers.get("x-forwarded-port")??""}return t.nextUrl.basePath&&(l.pathname=P(l.pathname,t.nextUrl.basePath)),T=!0,e.redirect(l.toString())}const z=m(j,L.locales,L.localePrefix),A=h(j,L.locales,L.localePrefix,w),C=null!=A,I="never"===L.localePrefix.mode||b&&"as-needed"===L.localePrefix.mode;let N,S,T,V=z;const B=L.pathnames;if(B){let e;if([e,S]=x(B,z,k),S){const r=B[S],a=n(r,k,S);if(o(a,z))V=p(z,a,S);else{let o;o=e?n(r,e,S):S;const s=I?void 0:l(k,L.localePrefix),i=p(z,o,a);N=H(u(i,s,t.nextUrl.search))}}}if(!N)if("/"!==V||C){const e=u(V,g(k),t.nextUrl.search);if(C){const r=u(z,A.prefix,t.nextUrl.search);if("never"===L.localePrefix.mode)N=H(u(z,void 0,t.nextUrl.search));else if(A.exact)if(b&&I)N=H(u(z,void 0,t.nextUrl.search));else if(L.domains){const t=U(w,A.locale,R);N=w?.domain===t?.domain||q?y(e):H(r,t?.domain)}else N=y(e);else N=H(r)}else N=I?y(e):H(u(z,l(k,L.localePrefix),t.nextUrl.search))}else N=I?y(u(V,g(k),t.nextUrl.search)):H(u(z,l(k,L.localePrefix),t.nextUrl.search));return c(t,N,k,L,w),!T&&"never"!==L.localePrefix.mode&&L.alternateLinks&&L.locales.length>1&&N.headers.set("Link",s({routing:L,internalTemplateName:S,localizedPathnames:null!=S&&B?B[S]:void 0,request:t,resolvedLocale:k})),N}}export{v as default};
@@ -1 +1 @@
1
- import{getSortedPathnames as e,matchesPathname as t,normalizeTrailingSlash as n,getLocalePrefix as r,templateToRegex as o,prefixPathname as c}from"../shared/utils.js";function i(n,r,o){const c=e(Object.keys(n));for(const e of c){const c=n[e];if("string"==typeof c){if(t(c,r))return[void 0,e]}else{const n=Object.entries(c),i=n.findIndex((([e])=>e===o));i>0&&n.unshift(n.splice(i,1)[0]);for(const[o,c]of n)if(t(c,r))return[o,e]}}for(const e of Object.keys(n))if(t(e,r))return[void 0,e];return[void 0,void 0]}function f(e,t,r,o){let c="";return c+=d(r,a(t,e)),c=n(c),c}function l(e,t,r){e.endsWith("/")||(e+="/");const o=s(t,r),c=new RegExp(`^(${o.map((([,e])=>e.replaceAll("/","\\/"))).join("|")})/(.*)`,"i"),i=e.match(c);let f=i?"/"+i[2]:e;return"/"!==f&&(f=n(f)),f}function s(e,t,n=!0){const o=e.map((e=>[e,r(e,t)]));return n&&o.sort(((e,t)=>t[1].length-e[1].length)),o}function u(e,t,n,r){const o=s(t,n);r&&o.sort((([e],[t])=>{if(e===r.defaultLocale)return-1;if(t===r.defaultLocale)return 1;const n=r.locales.includes(e),o=r.locales.includes(t);return n&&!o?-1:!n&&o?1:0}));for(const[t,n]of o){let r,o;if(e===n||e.startsWith(n+"/"))r=o=!0;else{const t=e.toLowerCase(),c=n.toLowerCase();(t===c||t.startsWith(c+"/"))&&(r=!1,o=!0)}if(o)return{locale:t,prefix:n,matchedPrefix:e.slice(0,n.length),exact:r}}}function a(e,t){const r=n(t),c=n(e),i=o(c).exec(r);if(!i)return;const f={};for(let e=1;e<i.length;e++){const t=c.match(/\[([^\]]+)\]/g)?.[e-1].replace(/[[\]]/g,"");t&&(f[t]=i[e])}return f}function d(e,t){if(!t)return e;let n=e=e.replace(/\[\[/g,"[").replace(/\]\]/g,"]");return Object.entries(t).forEach((([e,t])=>{n=n.replace(`[${e}]`,t)})),n}function h(e,t,n){let r=e;return t&&(r=c(t,r)),n&&(r+=n),r}function p(e){return e.get("x-forwarded-host")??e.get("host")??void 0}function g(e,t){return t.defaultLocale===e||t.locales.includes(e)}function x(e,t,n){let r;return e&&g(t,e)&&(r=e),r||(r=n.find((e=>e.defaultLocale===t))),r||(r=n.find((e=>e.locales.includes(t)))),r}function m(e,t){return n(t+e)}function j(e){return`/${e}`}function L(e){return e.replace(/\\/g,"%5C").replace(/\/+/g,"/")}export{m as applyBasePath,h as formatPathname,d as formatPathnameTemplate,f as formatTemplatePathname,x as getBestMatchingDomain,p as getHost,i as getInternalTemplate,j as getLocaleAsPrefix,s as getLocalePrefixes,l as getNormalizedPathname,u as getPathnameMatch,a as getRouteParams,g as isLocaleSupportedOnDomain,L as sanitizePathname};
1
+ import{getSortedPathnames as e,matchesPathname as t,normalizeTrailingSlash as n,getLocalePrefix as r,templateToRegex as o,prefixPathname as c,getLocalizedTemplate as i}from"../shared/utils.js";function s(n,r,o){const c=e(Object.keys(n));for(const e of c){const c=n[e];if("string"==typeof c){if(t(c,r))return[void 0,e]}else{const s=Object.entries(c),f=s.findIndex((([e])=>e===o));f>0&&s.unshift(s.splice(f,1)[0]);for(const[o]of s){const c=i(n[e],o,e);if(t(c,r))return[o,e]}}}for(const e of Object.keys(n))if(t(e,r))return[void 0,e];return[void 0,void 0]}function f(e,t,r,o){let c="";return c+=h(r,d(t,e)),c=n(c),c}function l(e,t,r){e.endsWith("/")||(e+="/");const o=u(t,r),c=new RegExp(`^(${o.map((([,e])=>e.replaceAll("/","\\/"))).join("|")})/(.*)`,"i"),i=e.match(c);let s=i?"/"+i[2]:e;return"/"!==s&&(s=n(s)),s}function u(e,t,n=!0){const o=e.map((e=>[e,r(e,t)]));return n&&o.sort(((e,t)=>t[1].length-e[1].length)),o}function a(e,t,n,r){const o=u(t,n);r&&o.sort((([e],[t])=>{if(e===r.defaultLocale)return-1;if(t===r.defaultLocale)return 1;const n=r.locales.includes(e),o=r.locales.includes(t);return n&&!o?-1:!n&&o?1:0}));for(const[t,n]of o){let r,o;if(e===n||e.startsWith(n+"/"))r=o=!0;else{const t=e.toLowerCase(),c=n.toLowerCase();(t===c||t.startsWith(c+"/"))&&(r=!1,o=!0)}if(o)return{locale:t,prefix:n,matchedPrefix:e.slice(0,n.length),exact:r}}}function d(e,t){const r=n(t),c=n(e),i=o(c).exec(r);if(!i)return;const s={};for(let e=1;e<i.length;e++){const t=c.match(/\[([^\]]+)\]/g)?.[e-1].replace(/[[\]]/g,"");t&&(s[t]=i[e])}return s}function h(e,t){if(!t)return e;let n=e=e.replace(/\[\[/g,"[").replace(/\]\]/g,"]");return Object.entries(t).forEach((([e,t])=>{n=n.replace(`[${e}]`,t)})),n}function p(e,t,n){let r=e;return t&&(r=c(t,r)),n&&(r+=n),r}function g(e){return e.get("x-forwarded-host")??e.get("host")??void 0}function x(e,t){return t.defaultLocale===e||t.locales.includes(e)}function m(e,t,n){let r;return e&&x(t,e)&&(r=e),r||(r=n.find((e=>e.defaultLocale===t))),r||(r=n.find((e=>e.locales.includes(t)))),r}function j(e,t){return n(t+e)}function L(e){return`/${e}`}function v(e){return e.replace(/\\/g,"%5C").replace(/\/+/g,"/")}export{j as applyBasePath,p as formatPathname,h as formatPathnameTemplate,f as formatTemplatePathname,m as getBestMatchingDomain,g as getHost,s as getInternalTemplate,L as getLocaleAsPrefix,u as getLocalePrefixes,l as getNormalizedPathname,a as getPathnameMatch,d as getRouteParams,x as isLocaleSupportedOnDomain,v as sanitizePathname};
@@ -1 +1 @@
1
- import{getSortedPathnames as e,matchesPathname as t,isLocalizableHref as n,prefixPathname as r,getLocalePrefix as o,normalizeTrailingSlash as a}from"../../shared/utils.js";function i(e){return"string"==typeof e?{pathname:e}:e}function c(e){function t(e){return String(e)}const n=new URLSearchParams;for(const[r,o]of Object.entries(e))Array.isArray(o)?o.forEach((e=>{n.append(r,t(e))})):n.set(r,t(o));return"?"+n.toString()}function s({pathname:e,locale:t,params:n,pathnames:r,query:o}){function i(e){let t=r[e];return t||(t=e),t}function s(e){let r="string"==typeof e?e:e[t];return n&&Object.entries(n).forEach((([e,t])=>{let n,o;Array.isArray(t)?(n=`(\\[)?\\[...${e}\\](\\])?`,o=t.map((e=>String(e))).join("/")):(n=`\\[${e}\\]`,o=String(t)),r=r.replace(new RegExp(n,"g"),o)})),r=r.replace(/\[\[\.\.\..+\]\]/g,""),r=a(r),o&&(r+=c(o)),r}if("string"==typeof e){return s(i(e))}{const{pathname:t,...n}=e;return{...n,pathname:s(i(t))}}}function f(n,r,o){const a=e(Object.keys(o)),i=decodeURI(r);for(const e of a){const r=o[e];if("string"==typeof r){if(t(r,i))return e}else if(t(r[n],i))return e}return r}function u(e,t=window.location.pathname){return"/"===e?t:t.replace(e,"")}function l(e,t,a,i){const{mode:c}=a.localePrefix;let s;return void 0!==i?s=i:n(e)&&("always"===c?s=!0:"as-needed"===c&&(s=a.domains?!a.domains.some((e=>e.defaultLocale===t)):t!==a.defaultLocale)),s?r(o(t,a.localePrefix),e):e}export{l as applyPathnamePrefix,s as compileLocalizedPathname,u as getBasePath,f as getRoute,i as normalizeNameOrNameWithParams,c as serializeSearchParams};
1
+ import{getSortedPathnames as e,matchesPathname as n,getLocalizedTemplate as t,isLocalizableHref as r,prefixPathname as o,getLocalePrefix as a,normalizeTrailingSlash 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:r,pathnames:o,query:a}){function c(e){let n=o[e];return n||(n=e),n}function f(e,o){let c=t(e,n,o);return r&&Object.entries(r).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=i(c),a&&(c+=s(a)),c}if("string"==typeof e){return f(c(e),e)}{const{pathname:n,...t}=e;return{...t,pathname:f(c(n),n)}}}function u(r,o,a){const i=e(Object.keys(a)),c=decodeURI(o);for(const e of i){const o=a[e];if("string"==typeof o){if(n(o,c))return e}else if(n(t(o,r,e),c))return e}return o}function l(e,n=window.location.pathname){return"/"===e?n:n.replace(e,"")}function p(e,n,t,i){const{mode:c}=t.localePrefix;let s;return void 0!==i?s=i:r(e)&&("always"===c?s=!0:"as-needed"===c&&(s=t.domains?!t.domains.some((e=>e.defaultLocale===n)):n!==t.defaultLocale)),s?o(a(n,t.localePrefix),e):e}export{p as applyPathnamePrefix,f as compileLocalizedPathname,l as getBasePath,u as getRoute,c as normalizeNameOrNameWithParams,s as serializeSearchParams};
@@ -1 +1 @@
1
- function n(n){return function(n){return"object"==typeof n?null==n.host&&null==n.hostname:!/^[a-z]+:/i.test(n)}(n)&&!function(n){const t="object"==typeof n?n.pathname:n;return null!=t&&!t.startsWith("/")}(n)}function t(n,t){return n.replace(new RegExp(`^${t}`),"")||"/"}function e(n,t){let e=n;return/^\/(\?.*)?$/.test(t)&&(t=t.slice(1)),e+=t,e}function r(n,t){return t===n||t.startsWith(`${n}/`)}function u(n){const t=function(){try{return"true"===process.env._next_intl_trailing_slash}catch{return!1}}();if("/"!==n){const e=n.endsWith("/");t&&!e?n+="/":!t&&e&&(n=n.slice(0,-1))}return n}function i(n,t){const e=u(n),r=u(t);return f(e).test(r)}function c(n,t){return"never"!==t.mode&&t.prefixes?.[n]||o(n)}function o(n){return"/"+n}function f(n){const t=n.replace(/\[\[(\.\.\.[^\]]+)\]\]/g,"?(.*)").replace(/\[(\.\.\.[^\]]+)\]/g,"(.+)").replace(/\[([^\]]+)\]/g,"([^/]+)");return new RegExp(`^${t}$`)}function s(n){return n.includes("[[...")}function l(n){return n.includes("[...")}function a(n){return n.includes("[")}function p(n,t){const e=n.split("/"),r=t.split("/"),u=Math.max(e.length,r.length);for(let n=0;n<u;n++){const t=e[n],u=r[n];if(!t&&u)return-1;if(t&&!u)return 1;if(t||u){if(!a(t)&&a(u))return-1;if(a(t)&&!a(u))return 1;if(!l(t)&&l(u))return-1;if(l(t)&&!l(u))return 1;if(!s(t)&&s(u))return-1;if(s(t)&&!s(u))return 1}}return 0}function h(n){return n.sort(p)}function g(n){return"function"==typeof n.then}export{o as getLocaleAsPrefix,c as getLocalePrefix,h as getSortedPathnames,r as hasPathnamePrefixed,n as isLocalizableHref,g as isPromise,i as matchesPathname,u as normalizeTrailingSlash,e as prefixPathname,f as templateToRegex,t as unprefixPathname};
1
+ function n(n){return function(n){return"object"==typeof n?null==n.host&&null==n.hostname:!/^[a-z]+:/i.test(n)}(n)&&!function(n){const t="object"==typeof n?n.pathname:n;return null!=t&&!t.startsWith("/")}(n)}function t(n,t){return n.replace(new RegExp(`^${t}`),"")||"/"}function e(n,t){let e=n;return/^\/(\?.*)?$/.test(t)&&(t=t.slice(1)),e+=t,e}function r(n,t){return t===n||t.startsWith(`${n}/`)}function u(n,t,e){return"string"==typeof n?n:n[t]||e}function i(n){const t=function(){try{return"true"===process.env._next_intl_trailing_slash}catch{return!1}}();if("/"!==n){const e=n.endsWith("/");t&&!e?n+="/":!t&&e&&(n=n.slice(0,-1))}return n}function c(n,t){const e=i(n),r=i(t);return s(e).test(r)}function o(n,t){return"never"!==t.mode&&t.prefixes?.[n]||f(n)}function f(n){return"/"+n}function s(n){const t=n.replace(/\[\[(\.\.\.[^\]]+)\]\]/g,"?(.*)").replace(/\[(\.\.\.[^\]]+)\]/g,"(.+)").replace(/\[([^\]]+)\]/g,"([^/]+)");return new RegExp(`^${t}$`)}function l(n){return n.includes("[[...")}function p(n){return n.includes("[...")}function a(n){return n.includes("[")}function h(n,t){const e=n.split("/"),r=t.split("/"),u=Math.max(e.length,r.length);for(let n=0;n<u;n++){const t=e[n],u=r[n];if(!t&&u)return-1;if(t&&!u)return 1;if(t||u){if(!a(t)&&a(u))return-1;if(a(t)&&!a(u))return 1;if(!p(t)&&p(u))return-1;if(p(t)&&!p(u))return 1;if(!l(t)&&l(u))return-1;if(l(t)&&!l(u))return 1}}return 0}function g(n){return n.sort(h)}function x(n){return"function"==typeof n.then}export{f as getLocaleAsPrefix,o as getLocalePrefix,u as getLocalizedTemplate,g as getSortedPathnames,r as hasPathnamePrefixed,n as isLocalizableHref,x as isPromise,c as matchesPathname,i as normalizeTrailingSlash,e as prefixPathname,s as templateToRegex,t as unprefixPathname};
@@ -4,9 +4,10 @@ import type { DomainsConfig, LocalePrefixMode, Locales, Pathnames } from '../rou
4
4
  /**
5
5
  * See https://developers.google.com/search/docs/specialty/international/localized-versions
6
6
  */
7
- export default function getAlternateLinksHeaderValue<AppLocales extends Locales, AppLocalePrefixMode extends LocalePrefixMode, AppPathnames extends Pathnames<AppLocales> | undefined, AppDomains extends DomainsConfig<AppLocales> | undefined>({ localizedPathnames, request, resolvedLocale, routing }: {
7
+ export default function getAlternateLinksHeaderValue<AppLocales extends Locales, AppLocalePrefixMode extends LocalePrefixMode, AppPathnames extends Pathnames<AppLocales> | undefined, AppDomains extends DomainsConfig<AppLocales> | undefined>({ internalTemplateName, localizedPathnames, request, resolvedLocale, routing }: {
8
8
  routing: Omit<ResolvedRoutingConfig<AppLocales, AppLocalePrefixMode, AppPathnames, AppDomains>, 'pathnames'>;
9
9
  request: NextRequest;
10
10
  resolvedLocale: AppLocales[number];
11
11
  localizedPathnames?: Pathnames<AppLocales>[string];
12
+ internalTemplateName?: string;
12
13
  }): string;
@@ -12,7 +12,7 @@ export type LocalePrefixConfigVerbose<AppLocales extends Locales, AppLocalePrefi
12
12
  mode: 'never';
13
13
  };
14
14
  export type LocalePrefix<AppLocales extends Locales = [], AppLocalePrefixMode extends LocalePrefixMode = 'always'> = AppLocalePrefixMode | LocalePrefixConfigVerbose<AppLocales, AppLocalePrefixMode>;
15
- export type Pathnames<AppLocales extends Locales> = Record<Pathname, Record<AppLocales[number], Pathname> | Pathname>;
15
+ export type Pathnames<AppLocales extends Locales> = Record<Pathname, Partial<Record<AppLocales[number], Pathname>> | Pathname>;
16
16
  export type DomainConfig<AppLocales extends Locales> = {
17
17
  defaultLocale: AppLocales[number];
18
18
  /** The domain name (e.g. "example.com", "www.example.com" or "fr.example.com"). Note that the `x-forwarded-host` or alternatively the `host` header will be used to determine the requested domain. */
@@ -1,10 +1,11 @@
1
1
  import type { LinkProps } from 'next/link.js';
2
- import type { LocalePrefixConfigVerbose, LocalePrefixMode, Locales } from '../routing/types.js';
2
+ import type { LocalePrefixConfigVerbose, LocalePrefixMode, Locales, Pathnames } from '../routing/types.js';
3
3
  type Href = LinkProps['href'];
4
4
  export declare function isLocalizableHref(href: Href): boolean;
5
5
  export declare function unprefixPathname(pathname: string, prefix: string): string;
6
6
  export declare function prefixPathname(prefix: string, pathname: string): string;
7
7
  export declare function hasPathnamePrefixed(prefix: string | undefined, pathname: string): boolean;
8
+ export declare function getLocalizedTemplate<AppLocales extends Locales>(pathnameConfig: Pathnames<AppLocales>[keyof Pathnames<AppLocales>], locale: AppLocales[number], internalTemplate: string): string;
8
9
  export declare function normalizeTrailingSlash(pathname: string): string;
9
10
  export declare function matchesPathname(
10
11
  /** E.g. `/users/[userId]-[userName]` */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "next-intl",
3
- "version": "4.0.0-beta-021e874",
3
+ "version": "4.0.0-beta-dea867b",
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.0-beta-021e874"
115
+ "use-intl": "4.0.0-beta-dea867b"
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": "83ba4bf6a7655546f78853424e4f7497ed077a53"
127
+ "gitHead": "d6b499d3c2e9d5b1fa7df908ac892c0f41d26d3b"
128
128
  }