chore: upgrade next-intl to version 4.0.0 and update type definitions
- Update `next-intl` dependency from version 3.26.5 to 4.0.0 - Modify global type definitions to include new `Locale` and `Messages` types - Refactor various components and pages to use `Locale` type for params - Enhance internationalization handling by integrating `hasLocale` checks - Clean up imports and ensure consistent usage of `next-intl` features
This commit is contained in:
parent
d7389eef47
commit
df1c75f56a
18
global.d.ts
vendored
18
global.d.ts
vendored
@ -1,8 +1,14 @@
|
||||
import en from './messages/en.json';
|
||||
import { routing } from '@/i18n/routing';
|
||||
import messages from './messages/en.json';
|
||||
|
||||
type Messages = typeof en;
|
||||
|
||||
declare global {
|
||||
// Use type safe message keys with `next-intl`
|
||||
interface IntlMessages extends Messages {}
|
||||
/**
|
||||
* next-intl 4.0.0
|
||||
*
|
||||
* https://github.com/amannn/next-intl/blob/main/examples/example-app-router/global.d.ts
|
||||
*/
|
||||
declare module 'next-intl' {
|
||||
interface AppConfig {
|
||||
Locale: (typeof routing.locales)[number];
|
||||
Messages: typeof messages;
|
||||
}
|
||||
}
|
||||
|
@ -51,7 +51,7 @@
|
||||
"mdast-util-toc": "^7.1.0",
|
||||
"motion": "^12.4.3",
|
||||
"next": "15.2.1",
|
||||
"next-intl": "^3.26.5",
|
||||
"next-intl": "^4.0.0",
|
||||
"next-plausible": "^3.12.4",
|
||||
"next-safe-action": "^7.10.4",
|
||||
"next-themes": "^0.4.4",
|
||||
|
37
pnpm-lock.yaml
generated
37
pnpm-lock.yaml
generated
@ -132,8 +132,8 @@ importers:
|
||||
specifier: 15.2.1
|
||||
version: 15.2.1(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
next-intl:
|
||||
specifier: ^3.26.5
|
||||
version: 3.26.5(next@15.2.1(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)
|
||||
specifier: ^4.0.0
|
||||
version: 4.0.0(next@15.2.1(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)(typescript@5.7.3)
|
||||
next-plausible:
|
||||
specifier: ^3.12.4
|
||||
version: 3.12.4(next@15.2.1(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
@ -2231,6 +2231,9 @@ packages:
|
||||
peerDependencies:
|
||||
react: ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
|
||||
'@schummar/icu-type-parser@1.21.5':
|
||||
resolution: {integrity: sha512-bXHSaW5jRTmke9Vd0h5P7BtWZG9Znqb8gSDxZnxaGSJnGwPLDPfS+3g0BKzeWqzgZPsIVZkM7m2tbo18cm5HBw==}
|
||||
|
||||
'@selderee/plugin-htmlparser2@0.11.0':
|
||||
resolution: {integrity: sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==}
|
||||
|
||||
@ -3641,11 +3644,15 @@ packages:
|
||||
resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
next-intl@3.26.5:
|
||||
resolution: {integrity: sha512-EQlCIfY0jOhRldiFxwSXG+ImwkQtDEfQeSOEQp6ieAGSLWGlgjdb/Ck/O7wMfC430ZHGeUKVKax8KGusTPKCgg==}
|
||||
next-intl@4.0.0:
|
||||
resolution: {integrity: sha512-l+I1PLAFrjzYzrc340n1vssDJ7pP1gtYT1jOWlRWIHkyrPdyosEIHPC+LiqJP4vWvWtCZzzqTn9AaBF+x5Ja8g==}
|
||||
peerDependencies:
|
||||
next: ^10.0.0 || ^11.0.0 || ^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0
|
||||
next: ^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 || ^19.0.0
|
||||
typescript: ^5.0.0
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
|
||||
next-plausible@3.12.4:
|
||||
resolution: {integrity: sha512-cD3+ixJxf8yBYvsideTxqli3fvrB7R4BXcvsNJz8Sm2X1QN039WfiXjCyNWkub4h5++rRs6fHhchUMnOuJokcg==}
|
||||
@ -4518,6 +4525,11 @@ packages:
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0
|
||||
|
||||
use-intl@4.0.0:
|
||||
resolution: {integrity: sha512-/fmC7haEMVNa0isXGRGUir56fD4I9LRnOgbeBmji+bow6U8pE7WD+2X2sjqh+0h3yJ0T36PA6JXZ6PlVeRyt8w==}
|
||||
peerDependencies:
|
||||
react: ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0
|
||||
|
||||
use-sidecar@1.1.3:
|
||||
resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==}
|
||||
engines: {node: '>=10'}
|
||||
@ -6293,6 +6305,8 @@ snapshots:
|
||||
dependencies:
|
||||
react: 19.0.0
|
||||
|
||||
'@schummar/icu-type-parser@1.21.5': {}
|
||||
|
||||
'@selderee/plugin-htmlparser2@0.11.0':
|
||||
dependencies:
|
||||
domhandler: 5.0.3
|
||||
@ -8054,13 +8068,15 @@ snapshots:
|
||||
|
||||
negotiator@1.0.0: {}
|
||||
|
||||
next-intl@3.26.5(next@15.2.1(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0):
|
||||
next-intl@4.0.0(next@15.2.1(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)(typescript@5.7.3):
|
||||
dependencies:
|
||||
'@formatjs/intl-localematcher': 0.5.10
|
||||
negotiator: 1.0.0
|
||||
next: 15.2.1(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
react: 19.0.0
|
||||
use-intl: 3.26.5(react@19.0.0)
|
||||
use-intl: 4.0.0(react@19.0.0)
|
||||
optionalDependencies:
|
||||
typescript: 5.7.3
|
||||
|
||||
next-plausible@3.12.4(next@15.2.1(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
|
||||
dependencies:
|
||||
@ -9109,6 +9125,13 @@ snapshots:
|
||||
intl-messageformat: 10.7.15
|
||||
react: 19.0.0
|
||||
|
||||
use-intl@4.0.0(react@19.0.0):
|
||||
dependencies:
|
||||
'@formatjs/fast-memoize': 2.2.6
|
||||
'@schummar/icu-type-parser': 1.21.5
|
||||
intl-messageformat: 10.7.15
|
||||
react: 19.0.0
|
||||
|
||||
use-sidecar@1.1.3(@types/react@19.0.9)(react@19.0.0):
|
||||
dependencies:
|
||||
detect-node-es: 1.1.0
|
||||
|
@ -14,7 +14,7 @@ import Testimonials from '@/components/blocks/testimonials/testimonials';
|
||||
import { getTranslations } from 'next-intl/server';
|
||||
|
||||
interface HomePageProps {
|
||||
params: Promise<{ locale: string }>;
|
||||
params: Promise<{ locale: Locale }>;
|
||||
}
|
||||
|
||||
export default async function HomePage(props: HomePageProps) {
|
||||
|
@ -3,7 +3,7 @@ import CallToAction2 from '@/components/blocks/call-to-action/call-to-action-2';
|
||||
import CallToAction3 from '@/components/blocks/call-to-action/call-to-action-3';
|
||||
|
||||
interface CallToActionPageProps {
|
||||
params: Promise<{ locale: string }>;
|
||||
params: Promise<{ locale: Locale }>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -6,7 +6,7 @@ import Content5 from '@/components/blocks/content/content-5';
|
||||
import Content6 from '@/components/blocks/content/content-6';
|
||||
|
||||
interface ContentPageProps {
|
||||
params: Promise<{ locale: string }>;
|
||||
params: Promise<{ locale: Locale }>;
|
||||
}
|
||||
|
||||
export default async function ContentPage(props: ContentPageProps) {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import FAQs from '@/components/blocks/faq/faqs';
|
||||
|
||||
interface FAQPageProps {
|
||||
params: Promise<{ locale: string }>;
|
||||
params: Promise<{ locale: Locale }>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -8,7 +8,7 @@ import Features8 from '@/components/blocks/features/features-8';
|
||||
import Features9 from '@/components/blocks/features/features-9';
|
||||
|
||||
interface FeaturesPageProps {
|
||||
params: Promise<{ locale: string }>;
|
||||
params: Promise<{ locale: Locale }>;
|
||||
}
|
||||
|
||||
export default async function FeaturesPage(props: FeaturesPageProps) {
|
||||
|
@ -4,7 +4,7 @@ import HeroSection3 from '@/components/blocks/hero/hero-section-3';
|
||||
import HeroSection4 from '@/components/blocks/hero/hero-section-4';
|
||||
|
||||
interface HeroPageProps {
|
||||
params: Promise<{ locale: string }>;
|
||||
params: Promise<{ locale: Locale }>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -5,7 +5,7 @@ import PricingComparator from '@/components/pricing-comparator';
|
||||
import { getTranslations } from 'next-intl/server';
|
||||
|
||||
interface PricingPageProps {
|
||||
params: Promise<{ locale: string }>;
|
||||
params: Promise<{ locale: Locale }>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -4,7 +4,7 @@ import Stats3 from '@/components/blocks/stats/stats-3';
|
||||
import Stats4 from '@/components/blocks/stats/stats-4';
|
||||
|
||||
interface StatsPageProps {
|
||||
params: Promise<{ locale: string }>;
|
||||
params: Promise<{ locale: Locale }>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -5,7 +5,7 @@ import Testimonials5 from '@/components/blocks/testimonials/testimonials-5';
|
||||
import Testimonials6 from '@/components/blocks/testimonials/testimonials-6';
|
||||
|
||||
interface TestimonialsPageProps {
|
||||
params: Promise<{ locale: string }>;
|
||||
params: Promise<{ locale: Locale }>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -13,7 +13,7 @@ import { getBaseUrl } from '@/lib/urls/get-base-url';
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ slug: string; locale: string }>;
|
||||
params: Promise<{ slug: string; locale: Locale }>;
|
||||
}): Promise<Metadata | undefined> {
|
||||
const resolvedParams = await params;
|
||||
const { slug, locale } = resolvedParams;
|
||||
|
@ -5,7 +5,7 @@ import PricingComparator from '@/components/pricing-comparator';
|
||||
import { getTranslations } from 'next-intl/server';
|
||||
|
||||
interface PricingPageProps {
|
||||
params: Promise<{ locale: string }>;
|
||||
params: Promise<{ locale: Locale }>;
|
||||
}
|
||||
|
||||
export default async function PricingPage(props: PricingPageProps) {
|
||||
|
@ -4,8 +4,7 @@ import { routing } from '@/i18n/routing';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { GeistMono } from 'geist/font/mono';
|
||||
import { GeistSans } from 'geist/font/sans';
|
||||
import { NextIntlClientProvider } from 'next-intl';
|
||||
import { getMessages } from 'next-intl/server';
|
||||
import { Locale, hasLocale, NextIntlClientProvider } from 'next-intl';
|
||||
import { notFound } from 'next/navigation';
|
||||
import { ReactNode } from 'react';
|
||||
import { Toaster } from 'sonner';
|
||||
@ -15,7 +14,7 @@ import '@/styles/globals.css';
|
||||
|
||||
interface LocaleLayoutProps {
|
||||
children: ReactNode;
|
||||
params: Promise<{ locale: string }>;
|
||||
params: Promise<{ locale: Locale }>;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -32,13 +31,10 @@ export default async function LocaleLayout({
|
||||
const { locale } = await params;
|
||||
|
||||
// Ensure that the incoming `locale` is valid
|
||||
if (!routing.locales.includes(locale as any)) {
|
||||
if (!hasLocale(routing.locales, locale)) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
// Providing all messages to the client side
|
||||
const messages = await getMessages();
|
||||
|
||||
return (
|
||||
<html lang={locale} suppressHydrationWarning>
|
||||
<body
|
||||
@ -53,7 +49,7 @@ export default async function LocaleLayout({
|
||||
GeistMono.variable
|
||||
)}
|
||||
>
|
||||
<NextIntlClientProvider messages={messages}>
|
||||
<NextIntlClientProvider>
|
||||
<Providers>
|
||||
{children}
|
||||
|
||||
|
@ -8,9 +8,9 @@ import {
|
||||
SelectValue,
|
||||
} from '@/components/ui/select';
|
||||
import { useLocalePathname, useLocaleRouter } from '@/i18n/navigation';
|
||||
import { DEFAULT_LOCALE, Locale, LOCALE_LIST, routing } from '@/i18n/routing';
|
||||
import { DEFAULT_LOCALE, LOCALE_LIST, routing } from '@/i18n/routing';
|
||||
import { useLocaleStore } from '@/stores/locale-store';
|
||||
import { useLocale } from 'next-intl';
|
||||
import { Locale, useLocale } from 'next-intl';
|
||||
import { useParams } from 'next/navigation';
|
||||
import { useEffect, useTransition } from 'react';
|
||||
|
||||
|
@ -8,10 +8,10 @@ import {
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
import { useLocalePathname, useLocaleRouter } from '@/i18n/navigation';
|
||||
import { Locale, LOCALE_LIST, routing } from '@/i18n/routing';
|
||||
import { LOCALE_LIST, routing } from '@/i18n/routing';
|
||||
import { useLocaleStore } from '@/stores/locale-store';
|
||||
import { Languages } from 'lucide-react';
|
||||
import { useLocale, useTranslations } from 'next-intl';
|
||||
import { Locale, useLocale, useTranslations } from 'next-intl';
|
||||
import { useParams } from 'next/navigation';
|
||||
import { useEffect, useTransition } from 'react';
|
||||
|
||||
|
@ -1,11 +1,8 @@
|
||||
import deepmerge from 'deepmerge';
|
||||
import { routing } from './routing';
|
||||
import { Locale, Messages } from 'next-intl';
|
||||
|
||||
import type messages from '../../messages/en.json';
|
||||
|
||||
export type Messages = typeof messages;
|
||||
|
||||
export const importLocale = async (locale: string): Promise<Messages> => {
|
||||
const importLocale = async (locale: Locale): Promise<Messages> => {
|
||||
return (await import(`../../messages/${locale}.json`)).default as Messages;
|
||||
};
|
||||
|
||||
@ -16,7 +13,7 @@ export const importLocale = async (locale: string): Promise<Messages> => {
|
||||
* https://next-intl.dev/docs/usage/configuration#messages
|
||||
*/
|
||||
export const getMessagesForLocale = async (
|
||||
locale: string
|
||||
locale: Locale
|
||||
): Promise<Messages> => {
|
||||
const localeMessages = await importLocale(locale);
|
||||
if (locale === routing.defaultLocale) {
|
||||
|
@ -5,6 +5,7 @@ import { routing } from './routing';
|
||||
* Navigation APIs
|
||||
*
|
||||
* https://next-intl.dev/docs/routing/navigation
|
||||
* https://github.com/amannn/next-intl/blob/main/examples/example-app-router/src/i18n/navigation.ts
|
||||
*/
|
||||
export const {
|
||||
Link: LocaleLink,
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { hasLocale } from 'next-intl';
|
||||
import { getRequestConfig } from 'next-intl/server';
|
||||
import { getMessagesForLocale } from './messages';
|
||||
import { routing } from './routing';
|
||||
@ -11,15 +12,17 @@ import { routing } from './routing';
|
||||
* The first component to use internationalization will call the function defined with getRequestConfig.
|
||||
*
|
||||
* https://next-intl.dev/docs/usage/configuration
|
||||
* https://github.com/amannn/next-intl/blob/main/examples/example-app-router/src/i18n/request.ts
|
||||
*/
|
||||
export default getRequestConfig(async ({ requestLocale }) => {
|
||||
// This typically corresponds to the `[locale]` segment
|
||||
let locale = await requestLocale;
|
||||
let requested = await requestLocale;
|
||||
|
||||
// Ensure that the incoming `locale` is valid
|
||||
if (!locale || !routing.locales.includes(locale as any)) {
|
||||
locale = routing.defaultLocale;
|
||||
}
|
||||
// https://next-intl.dev/blog/next-intl-4-0?s#strictly-typed-locale
|
||||
const locale = hasLocale(routing.locales, requested)
|
||||
? requested
|
||||
: routing.defaultLocale;
|
||||
|
||||
// https://next-intl.dev/docs/usage/configuration#messages
|
||||
// If you have incomplete messages for a given locale and would like to use messages
|
||||
|
@ -14,6 +14,7 @@ export const LOCALE_COOKIE_NAME = 'NEXT_LOCALE';
|
||||
* Next.js internationalized routing
|
||||
*
|
||||
* https://next-intl.dev/docs/routing
|
||||
* https://github.com/amannn/next-intl/blob/main/examples/example-app-router/src/i18n/routing.ts
|
||||
*/
|
||||
export const routing = defineRouting({
|
||||
// A list of all locales that are supported
|
||||
@ -46,4 +47,4 @@ export const routing = defineRouting({
|
||||
});
|
||||
|
||||
// export type Pathnames = keyof typeof routing.pathnames;
|
||||
export type Locale = (typeof routing.locales)[number];
|
||||
// export type Locale = (typeof routing.locales)[number];
|
||||
|
@ -6,7 +6,7 @@ import { allPages } from 'content-collections';
|
||||
* @param locale The locale to get the page for
|
||||
* @returns The custom page or undefined if not found
|
||||
*/
|
||||
export async function getCustomPage(type: string, locale: string) {
|
||||
export async function getCustomPage(type: string, locale: Locale) {
|
||||
// Find page with matching slug and locale
|
||||
const page = allPages.find(
|
||||
(page) => page.slugAsParams === `${type}` && page.locale === locale
|
||||
|
@ -5,7 +5,7 @@ import { allReleases } from 'content-collections';
|
||||
* @param locale The locale to get releases for
|
||||
* @returns An array of releases sorted by date (newest first)
|
||||
*/
|
||||
export async function getReleases(locale: string) {
|
||||
export async function getReleases(locale: Locale) {
|
||||
// Find all published releases with matching locale
|
||||
const releases = allReleases.filter(
|
||||
(release) => release.published && release.locale === locale
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { getWebsiteInfo } from '@/config';
|
||||
import { Locale, LOCALE_COOKIE_NAME, routing } from '@/i18n/routing';
|
||||
import { LOCALE_COOKIE_NAME, routing } from '@/i18n/routing';
|
||||
import { createTranslator } from '@/i18n/translator';
|
||||
import { type ClassValue, clsx } from 'clsx';
|
||||
import { parse as parseCookies } from 'cookie';
|
||||
import { Locale } from 'next-intl';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
@ -19,7 +20,7 @@ export function cn(...inputs: ClassValue[]) {
|
||||
export function createTitle(
|
||||
title: string,
|
||||
addSuffix: boolean = true,
|
||||
locale: string = 'en'
|
||||
locale: Locale = routing.defaultLocale
|
||||
): string {
|
||||
// Create a simple translator function for default values
|
||||
const t = createTranslator((key: string) => key);
|
||||
|
@ -1,9 +1,9 @@
|
||||
import type { Messages } from '@/i18n/messages';
|
||||
import { getMessagesForLocale } from '@/i18n/messages';
|
||||
import { Locale, routing } from '@/i18n/routing';
|
||||
import { routing } from '@/i18n/routing';
|
||||
import { mailTemplates } from '@/mail/emails';
|
||||
import { sendEmail } from '@/mail/provider/resend';
|
||||
import { render } from '@react-email/render';
|
||||
import { Locale, Messages } from 'next-intl';
|
||||
|
||||
type Template = keyof typeof mailTemplates;
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
import type { Locale } from '@/i18n/routing';
|
||||
import type { Messages } from '@/i18n/messages';
|
||||
import { Locale, Messages } from 'next-intl';
|
||||
|
||||
export interface EmailParams {
|
||||
to: string;
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { MetadataRoute } from 'next';
|
||||
import { routing, Locale } from '@/i18n/routing';
|
||||
import { routing } from '@/i18n/routing';
|
||||
import { getLocalePathname } from '@/i18n/navigation';
|
||||
import { getBaseUrl } from './lib/urls/get-base-url';
|
||||
import { Locale } from 'next-intl';
|
||||
|
||||
/**
|
||||
* https://github.com/javayhu/cnblocks/blob/main/app/sitemap.ts
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Locale } from '@/i18n/routing';
|
||||
import { Locale } from 'next-intl';
|
||||
import { create } from 'zustand';
|
||||
|
||||
interface LocaleState {
|
||||
|
Loading…
Reference in New Issue
Block a user