next-intl 4.14.2 β†’ 4.14.4

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
@@ -24,6 +24,8 @@ Internationalization (i18n) is an essential part of the user experience, therefo
24
24
  - πŸš€ **Next.js-native and performance-obsessed**: App Router, Server Components, static renderingβ€”pick the right tool for the right job, next-intl works everywhere.
25
25
  - 🌍 **Internationalized routing**: Provide unique pathnames per language and optionally localize pathnames for search engine optimization.
26
26
 
27
+ [β†’ Read the docs](https://next-intl.dev)
28
+
27
29
  ## What does it look like?
28
30
 
29
31
  ```jsx
@@ -58,7 +60,35 @@ export default function UserProfile({user}) {
58
60
  }
59
61
  ```
60
62
 
61
- ### [β†’ Read the docs](https://next-intl.dev)
63
+ ## Lint your messages
64
+
65
+ As an app grows, messages may drift and become inconsistent.
66
+
67
+ A companion tool, [`eloqnt/cli`](https://cli.eloqnt.dev/docs), catches this by analyzing your source code and messages statically:
68
+
69
+ ```console
70
+ $ eloqnt lint
71
+
72
+ messages/de.json
73
+ β”‚
74
+ β”‚ "UserProfile.title": "Dein Profil"
75
+ β”‚ ─┬──────────
76
+ β”‚ ╰─ Inconsistent ICU arguments: missing {firstName} (inconsistent-args)
77
+ β”‚
78
+ β”‚ "UserProfile.membership": "Member since {memberSince, date, short}"
79
+ β”‚ ─┬──────────────────────
80
+ β”‚ ╰─ Missing translation for de (missing-translation)
81
+
82
+ βœ— 1 error
83
+ ! 1 warning
84
+
85
+ β†’ Rule details: https://cli.eloqnt.dev/docs/lint-rules/<rule>
86
+ β†’ Run `eloqnt translate` to fill in 1 missing translation
87
+ ```
88
+
89
+ It can optionally also fill in missing translations with [`eloqnt translate`](https://cli.eloqnt.dev/docs/cli/translate), using your source code as context.
90
+
91
+ ---
62
92
 
63
93
  <div align="center">
64
94
  <a href="https://next-intl.dev/redirect?href=https://crowdin.com" target="_blank">
@@ -139,7 +139,7 @@ function formatPathnameTemplate(template, params) {
139
139
  template = template.replace(/\[\[/g, '[').replace(/\]\]/g, ']');
140
140
  let result = template;
141
141
  Object.entries(params).forEach(([key, value]) => {
142
- result = result.replace(`[${key}]`, value);
142
+ result = result.replace(`[${key}]`, () => value);
143
143
  });
144
144
  return result;
145
145
  }
@@ -51,7 +51,7 @@ function compileLocalizedPathname({
51
51
  regexp = `\\[${key}\\]`;
52
52
  replacer = String(paramValue);
53
53
  }
54
- compiled = compiled.replace(new RegExp(regexp, 'g'), replacer);
54
+ compiled = compiled.replace(new RegExp(regexp, 'g'), () => replacer);
55
55
  });
56
56
  }
57
57
 
@@ -133,8 +133,14 @@ function getRoute(locale, pathname, pathnames) {
133
133
  function getBasePath(pathname, windowPathname = window.location.pathname) {
134
134
  if (pathname === '/') {
135
135
  return windowPathname;
136
+ } else if (windowPathname.endsWith(pathname)) {
137
+ // Only strip the trailing occurrence: the pathname can also
138
+ // appear inside the base path itself (e.g. `/dashboard/dash`)
139
+ return windowPathname.slice(0, -pathname.length);
136
140
  } else {
137
- return windowPathname.replace(pathname, '');
141
+ // Unexpected, since the window pathname should always end with
142
+ // the pathname. Assuming no base path is safe though.
143
+ return '';
138
144
  }
139
145
  }
140
146
  function applyPathnamePrefix(pathname, locale, routing, force) {
@@ -1 +1 @@
1
- import{normalizeTrailingSlash as e,getSortedPathnames as t,matchesPathname as n,prefixPathname as r,getLocalePrefix as o,templateToRegex as c,getLocalizedTemplate as i}from"../shared/utils.js";function s(e,r,o){const c=t(Object.keys(e));for(const t of c){const c=e[t];if("string"==typeof c){if(n(c,r))return[void 0,t]}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(e[t],o,t);if(n(c,r))return[o,t]}}}for(const t of Object.keys(e))if(n(t,r))return[void 0,t];return[void 0,void 0]}function f(t,n,r,o){let c="";return c+=p(r,d(n,t)),c=e(c),c}function l(t,n,r){t.endsWith("/")||(t+="/");const o=u(n,r),c=new RegExp(`^(${o.map((([,e])=>e.replaceAll("/","\\/"))).join("|")})/(.*)`,"i"),i=t.match(c);let s=i?"/"+i[2]:t;return"/"!==s&&(s=e(s)),s}function u(e,t,n=!0){const r=e.map((e=>[e,o(e,t)]));return n&&r.sort(((e,t)=>t[1].length-e[1].length)),r}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(t,n){const r=e(n),o=e(t),i=c(o).exec(r);if(!i)return;const s={},f=o.match(/\[([^\]]+)\]/g)??[];for(let e=1;e<i.length;e++){const t=f[e-1];if(!t)continue;const n=t.replace(/[[\]]/g,""),r=i[e]??"";s[n]=r}return s}function p(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 g(e,t,n){let o=e;return t&&(o=r(t,o)),n&&(o+=n),o}function h(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(t,n){return e(n+t)}function L(e){return`/${e}`}function v(e){return e.replace(/\\/g,"%5C").replace(/[\t\n\r]/g,"").replace(/\/+/g,"/")}export{j as applyBasePath,g as formatPathname,p as formatPathnameTemplate,f as formatTemplatePathname,m as getBestMatchingDomain,h 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
+ import{normalizeTrailingSlash as e,getSortedPathnames as t,matchesPathname as n,prefixPathname as r,getLocalePrefix as o,templateToRegex as c,getLocalizedTemplate as i}from"../shared/utils.js";function s(e,r,o){const c=t(Object.keys(e));for(const t of c){const c=e[t];if("string"==typeof c){if(n(c,r))return[void 0,t]}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(e[t],o,t);if(n(c,r))return[o,t]}}}for(const t of Object.keys(e))if(n(t,r))return[void 0,t];return[void 0,void 0]}function f(t,n,r,o){let c="";return c+=p(r,d(n,t)),c=e(c),c}function l(t,n,r){t.endsWith("/")||(t+="/");const o=u(n,r),c=new RegExp(`^(${o.map((([,e])=>e.replaceAll("/","\\/"))).join("|")})/(.*)`,"i"),i=t.match(c);let s=i?"/"+i[2]:t;return"/"!==s&&(s=e(s)),s}function u(e,t,n=!0){const r=e.map((e=>[e,o(e,t)]));return n&&r.sort(((e,t)=>t[1].length-e[1].length)),r}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(t,n){const r=e(n),o=e(t),i=c(o).exec(r);if(!i)return;const s={},f=o.match(/\[([^\]]+)\]/g)??[];for(let e=1;e<i.length;e++){const t=f[e-1];if(!t)continue;const n=t.replace(/[[\]]/g,""),r=i[e]??"";s[n]=r}return s}function p(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 g(e,t,n){let o=e;return t&&(o=r(t,o)),n&&(o+=n),o}function h(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(t,n){return e(n+t)}function L(e){return`/${e}`}function v(e){return e.replace(/\\/g,"%5C").replace(/[\t\n\r]/g,"").replace(/\/+/g,"/")}export{j as applyBasePath,g as formatPathname,p as formatPathnameTemplate,f as formatTemplatePathname,m as getBestMatchingDomain,h 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 n,isLocalizableHref as t,prefixPathname as r,normalizeTrailingSlash as o,getLocalizedTemplate 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){const c=r[e];let f;if(c){const r=a(c,n,e);f=r,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)),f=f.replace(new RegExp(t,"g"),r)})),f=f.replace(/\[\[\.\.\..+\]\]/g,""),f=function(e){return new URL(e,"http://l").pathname}(f)}else f=e;return f=o(f),i&&(f+=s(i)),f}if("string"==typeof e)return c(e);{const{pathname:n,...t}=e;return{...t,pathname:c(n)}}}function l(t,r,o){const i=e(Object.keys(o)),c=decodeURI(r);for(const e of i){const r=o[e];if("string"==typeof r){if(n(r,c))return e}else if(n(a(r,t,e),c))return e}return r}function u(e,n=window.location.pathname){return"/"===e?n:n.replace(e,"")}function p(e,n,o,a){const{mode:c}=o.localePrefix;let s;if(void 0!==a)s=a;else if(t(e)){const e=o.domains?.find((e=>e.locales.includes(n))),t=e?.localePrefix||c;"always"===t?s=!0:"as-needed"===t&&(s=e?n!==e.defaultLocale:n!==o.defaultLocale)}return s?r(i(n,o.localePrefix),e):e}export{p as applyPathnamePrefix,f as compileLocalizedPathname,u as getBasePath,l as getRoute,c as normalizeNameOrNameWithParams,s as serializeSearchParams};
1
+ import{getSortedPathnames as e,matchesPathname as n,isLocalizableHref as t,prefixPathname as r,normalizeTrailingSlash as o,getLocalizedTemplate 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){const c=r[e];let f;if(c){const r=a(c,n,e);f=r,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)),f=f.replace(new RegExp(t,"g"),(()=>r))})),f=f.replace(/\[\[\.\.\..+\]\]/g,""),f=function(e){return new URL(e,"http://l").pathname}(f)}else f=e;return f=o(f),i&&(f+=s(i)),f}if("string"==typeof e)return c(e);{const{pathname:n,...t}=e;return{...t,pathname:c(n)}}}function l(t,r,o){const i=e(Object.keys(o)),c=decodeURI(r);for(const e of i){const r=o[e];if("string"==typeof r){if(n(r,c))return e}else if(n(a(r,t,e),c))return e}return r}function u(e,n=window.location.pathname){return"/"===e?n:n.endsWith(e)?n.slice(0,-e.length):""}function p(e,n,o,a){const{mode:c}=o.localePrefix;let s;if(void 0!==a)s=a;else if(t(e)){const e=o.domains?.find((e=>e.locales.includes(n))),t=e?.localePrefix||c;"always"===t?s=!0:"as-needed"===t&&(s=e?n!==e.defaultLocale:n!==o.defaultLocale)}return s?r(i(n,o.localePrefix),e):e}export{p as applyPathnamePrefix,f as compileLocalizedPathname,u as getBasePath,l 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.14.2",
3
+ "version": "4.14.4",
4
4
  "sideEffects": false,
5
5
  "author": "Jan Amann <jan@amann.work>",
6
6
  "funding": [
@@ -130,14 +130,14 @@
130
130
  "@formatjs/intl-localematcher": "^0.8.1",
131
131
  "@parcel/watcher": "^2.4.1",
132
132
  "@swc/core": "~1.16.0",
133
- "icu-minify": "^4.14.2",
133
+ "icu-minify": "^4.14.4",
134
134
  "negotiator": "^1.0.0",
135
- "next-intl-swc-plugin-extractor": "4.14.2",
136
- "use-intl": "^4.14.2"
135
+ "next-intl-swc-plugin-extractor": "4.14.4",
136
+ "use-intl": "^4.14.4"
137
137
  },
138
138
  "peerDependencies": {
139
139
  "next": "^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0",
140
140
  "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0"
141
141
  },
142
- "gitHead": "3259beb76120614af8b48e814c56324ad71ccb61"
142
+ "gitHead": "8267c8f16d6c503abff7a3d909a89a0c6c862049"
143
143
  }