From 344da5bc268df2b2362beff72eac89f6ab376ab8 Mon Sep 17 00:00:00 2001 From: Jan Amann Date: Mon, 11 Nov 2024 17:38:53 +0100 Subject: [PATCH] chore: Upgrade `next-intl` to 3.24, migrate deprecated APIs (#7177) * chore: Upgrade next-intl to 3.24, migrate deprecated APIs * reduce changes * revert another change * fix: Read locale from params in layout * address review points --------- Co-authored-by: Claudio W --- apps/site/app/[locale]/[[...path]]/page.tsx | 6 +- apps/site/app/[locale]/layout.tsx | 11 +- apps/site/components/__mocks__/next-intl.mjs | 2 +- .../hooks/react-generic/useSiteNavigation.ts | 2 +- apps/site/i18n.tsx | 27 +++-- apps/site/navigation.mjs | 7 +- apps/site/package.json | 2 +- package-lock.json | 105 +++++++++--------- 8 files changed, 90 insertions(+), 72 deletions(-) diff --git a/apps/site/app/[locale]/[[...path]]/page.tsx b/apps/site/app/[locale]/[[...path]]/page.tsx index 14aff4e2d41b4..f847b487c9b7f 100644 --- a/apps/site/app/[locale]/[[...path]]/page.tsx +++ b/apps/site/app/[locale]/[[...path]]/page.tsx @@ -1,6 +1,6 @@ import { setContext, setTags } from '@sentry/nextjs'; import { notFound, redirect } from 'next/navigation'; -import { unstable_setRequestLocale } from 'next-intl/server'; +import { setRequestLocale } from 'next-intl/server'; import type { FC } from 'react'; import { setClientContext } from '@/client-context'; @@ -69,7 +69,7 @@ const getPage: FC = async ({ params }) => { if (!availableLocaleCodes.includes(locale)) { // Forces the current locale to be the Default Locale - unstable_setRequestLocale(defaultLocale.code); + setRequestLocale(defaultLocale.code); if (!allLocaleCodes.includes(locale)) { // when the locale is not listed in the locales, return NotFound @@ -82,7 +82,7 @@ const getPage: FC = async ({ params }) => { } // Configures the current Locale to be the given Locale of the Request - unstable_setRequestLocale(locale); + setRequestLocale(locale); // Gets the current full pathname for a given path const pathname = dynamicRouter.getPathname(path); diff --git a/apps/site/app/[locale]/layout.tsx b/apps/site/app/[locale]/layout.tsx index 87d0f28dd1801..6ab90c90b3f87 100644 --- a/apps/site/app/[locale]/layout.tsx +++ b/apps/site/app/[locale]/layout.tsx @@ -1,7 +1,6 @@ import { Analytics } from '@vercel/analytics/react'; import { SpeedInsights } from '@vercel/speed-insights/next'; import classNames from 'classnames'; -import { getLocale } from 'next-intl/server'; import type { FC, PropsWithChildren } from 'react'; import BaseLayout from '@/layouts/Base'; @@ -15,8 +14,14 @@ import '@/styles/index.css'; const fontClasses = classNames(IBM_PLEX_MONO.variable, OPEN_SANS.variable); -const RootLayout: FC = async ({ children }) => { - const locale = await getLocale(); +const RootLayout: FC< + PropsWithChildren<{ + params: Promise<{ + locale: string; + }>; + }> +> = async ({ children, params }) => { + const { locale } = await params; const { langDir, hrefLang } = availableLocalesMap[locale] || defaultLocale; diff --git a/apps/site/components/__mocks__/next-intl.mjs b/apps/site/components/__mocks__/next-intl.mjs index a84400384381c..04af07d5abbac 100644 --- a/apps/site/components/__mocks__/next-intl.mjs +++ b/apps/site/components/__mocks__/next-intl.mjs @@ -27,7 +27,7 @@ export const useFormatter = () => { export const NextIntlClientProvider = ({ children }) => children; -export const createSharedPathnamesNavigation = () => ({ +export const createNavigation = () => ({ Link: Link, redirect: redirect, usePathname: usePathname, diff --git a/apps/site/hooks/react-generic/useSiteNavigation.ts b/apps/site/hooks/react-generic/useSiteNavigation.ts index 83b5403a4a883..4b11af46f0dd2 100644 --- a/apps/site/hooks/react-generic/useSiteNavigation.ts +++ b/apps/site/hooks/react-generic/useSiteNavigation.ts @@ -39,7 +39,7 @@ const useSiteNavigation = () => { const mapNavigationEntries = (entries: Navigation, context: Context = {}) => { const getFormattedMessage = (label: string, key: string) => - t.rich(label, context[key] || {}); + t.rich(label, context[key] || {}) as FormattedMessage; return Object.entries(entries).map( ([key, { label, link, items, target }]): [ diff --git a/apps/site/i18n.tsx b/apps/site/i18n.tsx index efed3f4d9c6fd..88cd1cf3e9341 100644 --- a/apps/site/i18n.tsx +++ b/apps/site/i18n.tsx @@ -1,7 +1,7 @@ import { importLocale } from '@node-core/website-i18n'; import { getRequestConfig } from 'next-intl/server'; -import { availableLocaleCodes } from '@/next.locales.mjs'; +import { availableLocaleCodes, defaultLocale } from '@/next.locales.mjs'; import deepMerge from './util/deepMerge'; @@ -13,7 +13,7 @@ const loadLocaleDictionary = async (locale: string) => { '@node-core/website-i18n/locales/en.json' ).then(f => f.default); - if (locale === 'en') { + if (locale === defaultLocale.code) { return defaultMessages; } @@ -30,9 +30,20 @@ const loadLocaleDictionary = async (locale: string) => { }; // Provides `next-intl` configuration for RSC/SSR -export default getRequestConfig(async ({ locale }) => ({ - // This is the dictionary of messages to be loaded - messages: await loadLocaleDictionary(locale), - // We always define the App timezone as UTC - timeZone: 'Etc/UTC', -})); +export default getRequestConfig(async ({ requestLocale }) => { + // This typically corresponds to the `[locale]` segment + let locale = await requestLocale; + + // Ensure that the incoming locale is valid + if (!locale || !availableLocaleCodes.includes(locale)) { + locale = defaultLocale.code; + } + + return { + locale, + // This is the dictionary of messages to be loaded + messages: await loadLocaleDictionary(locale), + // We always define the App timezone as UTC + timeZone: 'Etc/UTC', + }; +}); diff --git a/apps/site/navigation.mjs b/apps/site/navigation.mjs index edff2d9a0c197..b551ea2bcc390 100644 --- a/apps/site/navigation.mjs +++ b/apps/site/navigation.mjs @@ -1,8 +1,9 @@ 'use strict'; -import { createSharedPathnamesNavigation } from 'next-intl/navigation'; +import { createNavigation } from 'next-intl/navigation'; import { availableLocaleCodes } from './next.locales.mjs'; -export const { Link, redirect, usePathname, useRouter } = - createSharedPathnamesNavigation({ locales: availableLocaleCodes }); +export const { Link, redirect, usePathname, useRouter } = createNavigation({ + locales: availableLocaleCodes, +}); diff --git a/apps/site/package.json b/apps/site/package.json index 06ddf031da8df..5d59740ae6221 100644 --- a/apps/site/package.json +++ b/apps/site/package.json @@ -68,7 +68,7 @@ "glob": "~11.0.0", "gray-matter": "~4.0.3", "next": "~14.2.14", - "next-intl": "~3.21.1", + "next-intl": "~3.24.0", "next-themes": "~0.3.0", "postcss": "~8.4.47", "postcss-calc": "~10.0.2", diff --git a/package-lock.json b/package-lock.json index b79be4f9d634b..22977e34ec1fd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -66,7 +66,7 @@ "glob": "~11.0.0", "gray-matter": "~4.0.3", "next": "~14.2.14", - "next-intl": "~3.21.1", + "next-intl": "~3.24.0", "next-themes": "~0.3.0", "postcss": "~8.4.47", "postcss-calc": "~10.0.2", @@ -3031,53 +3031,48 @@ "license": "MIT" }, "node_modules/@formatjs/ecma402-abstract": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.2.0.tgz", - "integrity": "sha512-IpM+ev1E4QLtstniOE29W1rqH9eTdx5hQdNL8pzrflMj/gogfaoONZqL83LUeQScHAvyMbpqP5C9MzNf+fFwhQ==", - "license": "MIT", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.2.3.tgz", + "integrity": "sha512-aElGmleuReGnk2wtYOzYFmNWYoiWWmf1pPPCYg0oiIQSJj0mjc4eUfzUXaSOJ4S8WzI/cLqnCTWjqz904FT2OQ==", "dependencies": { - "@formatjs/fast-memoize": "2.2.1", - "@formatjs/intl-localematcher": "0.5.5", - "tslib": "^2.7.0" + "@formatjs/fast-memoize": "2.2.3", + "@formatjs/intl-localematcher": "0.5.7", + "tslib": "2" } }, "node_modules/@formatjs/fast-memoize": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.1.tgz", - "integrity": "sha512-XS2RcOSyWxmUB7BUjj3mlPH0exsUzlf6QfhhijgI941WaJhVxXQ6mEWkdUFIdnKi3TuTYxRdelsgv3mjieIGIA==", - "license": "MIT", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.3.tgz", + "integrity": "sha512-3jeJ+HyOfu8osl3GNSL4vVHUuWFXR03Iz9jjgI7RwjG6ysu/Ymdr0JRCPHfF5yGbTE6JCrd63EpvX1/WybYRbA==", "dependencies": { - "tslib": "^2.7.0" + "tslib": "2" } }, "node_modules/@formatjs/icu-messageformat-parser": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.8.0.tgz", - "integrity": "sha512-r2un3fmF9oJv3mOkH+wwQZ037VpqmdfahbcCZ9Lh+p6Sx+sNsonI7Zcr6jNMm1s+Si7ejQORS4Ezlh05mMPAXA==", - "license": "MIT", + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.9.3.tgz", + "integrity": "sha512-9L99QsH14XjOCIp4TmbT8wxuffJxGK8uLNO1zNhLtcZaVXvv626N0s4A2qgRCKG3dfYWx9psvGlFmvyVBa6u/w==", "dependencies": { - "@formatjs/ecma402-abstract": "2.2.0", - "@formatjs/icu-skeleton-parser": "1.8.4", - "tslib": "^2.7.0" + "@formatjs/ecma402-abstract": "2.2.3", + "@formatjs/icu-skeleton-parser": "1.8.7", + "tslib": "2" } }, "node_modules/@formatjs/icu-skeleton-parser": { - "version": "1.8.4", - "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.4.tgz", - "integrity": "sha512-LMQ1+Wk1QSzU4zpd5aSu7+w5oeYhupRwZnMQckLPRYhSjf2/8JWQ882BauY9NyHxs5igpuQIXZDgfkaH3PoATg==", - "license": "MIT", + "version": "1.8.7", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.7.tgz", + "integrity": "sha512-fI+6SmS2g7h3srfAKSWa5dwreU5zNEfon2uFo99OToiLF6yxGE+WikvFSbsvMAYkscucvVmTYNlWlaDPp0n5HA==", "dependencies": { - "@formatjs/ecma402-abstract": "2.2.0", - "tslib": "^2.7.0" + "@formatjs/ecma402-abstract": "2.2.3", + "tslib": "2" } }, "node_modules/@formatjs/intl-localematcher": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.5.tgz", - "integrity": "sha512-t5tOGMgZ/i5+ALl2/offNqAQq/lfUnKLEw0mXQI4N4bqpedhrSE+fyKLpwnd22sK0dif6AV+ufQcTsKShB9J1g==", - "license": "MIT", + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.7.tgz", + "integrity": "sha512-GGFtfHGQVFe/niOZp24Kal5b2i36eE2bNL0xi9Sg/yd0TR8aLjcteApZdHmismP5QQax1cMnZM9yWySUUjJteA==", "dependencies": { - "tslib": "^2.7.0" + "tslib": "2" } }, "node_modules/@heroicons/react": { @@ -16165,15 +16160,14 @@ } }, "node_modules/intl-messageformat": { - "version": "10.7.1", - "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.1.tgz", - "integrity": "sha512-xQuJW2WcyzNJZWUu5xTVPOmNSA1Sowuu/NKFdUid5Fxx/Yl6/s4DefTU/y7zy+irZLDmFGmTLtnM8FqpN05wlA==", - "license": "BSD-3-Clause", + "version": "10.7.5", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.5.tgz", + "integrity": "sha512-CflbRvJiahVmnfxq/lO+DCM1/8ji4vC4rTnz6ZJEKKodViB+EWgY9M4EqXVRQ+3K0Ng5qwSyqybPP+KSfS4KZw==", "dependencies": { - "@formatjs/ecma402-abstract": "2.2.0", - "@formatjs/fast-memoize": "2.2.1", - "@formatjs/icu-messageformat-parser": "2.8.0", - "tslib": "^2.7.0" + "@formatjs/ecma402-abstract": "2.2.3", + "@formatjs/fast-memoize": "2.2.3", + "@formatjs/icu-messageformat-parser": "2.9.3", + "tslib": "2" } }, "node_modules/invariant": { @@ -21625,6 +21619,7 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -21687,24 +21682,31 @@ } }, "node_modules/next-intl": { - "version": "3.21.1", - "resolved": "https://registry.npmjs.org/next-intl/-/next-intl-3.21.1.tgz", - "integrity": "sha512-hQm4Wgq5i1lfOHAWmXBVl5d2/XAeddcjsrUmjotXEESzPSvW5j2t0Pr8AV8WorTILgqU748aXuenBhz5P78tdw==", + "version": "3.24.0", + "resolved": "https://registry.npmjs.org/next-intl/-/next-intl-3.24.0.tgz", + "integrity": "sha512-48X68QsI92grir2dH1W15yhyVnEjW4c9qmwNt+du+k6mI1QtlE6GyANWHoL4/leTixHv8knZ1y9B/Ys06gmKLg==", "funding": [ { "type": "individual", "url": "https://github.com/sponsors/amannn" } ], - "license": "MIT", "dependencies": { "@formatjs/intl-localematcher": "^0.5.4", - "negotiator": "^0.6.3", - "use-intl": "^3.21.1" + "negotiator": "^1.0.0", + "use-intl": "^3.24.0" }, "peerDependencies": { - "next": "^10.0.0 || ^11.0.0 || ^12.0.0 || ^13.0.0 || ^14.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + "next": "^10.0.0 || ^11.0.0 || ^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0" + } + }, + "node_modules/next-intl/node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "engines": { + "node": ">= 0.6" } }, "node_modules/next-themes": { @@ -28926,16 +28928,15 @@ } }, "node_modules/use-intl": { - "version": "3.22.0", - "resolved": "https://registry.npmjs.org/use-intl/-/use-intl-3.22.0.tgz", - "integrity": "sha512-SoiPcyLJODhenrbDkcYJuOImgrBFN7Z8keLSHe7ffsNkIJtjdjet/RmqAv5Ym9TVxPpCs+fH2cl1J3YzFJSkWw==", - "license": "MIT", + "version": "3.24.0", + "resolved": "https://registry.npmjs.org/use-intl/-/use-intl-3.24.0.tgz", + "integrity": "sha512-lmrARod7yjMYehbyY9xBLjjgnlNcJsl1UAltAPlgspRG7RH6H0JYaGo4C3PZW/BTy0Dgmcvcl8rH/VemzGIhgQ==", "dependencies": { "@formatjs/fast-memoize": "^2.2.0", "intl-messageformat": "^10.5.14" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0" } }, "node_modules/use-sidecar": {