Nextjs接入多语言

899 阅读2分钟

image.png

总结一下新的Nextjs项目快速接入多语言配置

项目目录:

/
├── README.md
├── messages
│   ├── ar.json
│   ├── en.json
│   ├── ru.json
│   └── zh.json
├── next.config.js
├── package.json
├── pnpm-lock.yaml
├── postcss.config.js
├── src
│   ├── app
│   │   ├── [locale]
│   │   │   ├── layout.tsx
│   │   │   ├── page.tsx
│   │   │   ├── about
│   │   │   │   └── page.tsx
│   │   │   ├── price
│   │   │   │   └── page.tsx
│   ├── components
│   │   └── LanguageSwitcher.tsx //语言切换组件
│   ├── globals.css
│   ├── i18n.ts
│   ├── middleware.ts
│   └── navigation.ts
├── tailwind.config.ts
└── tsconfig.json
  1. 将页面都已入到src->app->[locale]目录下,这样切换语言同一个页面的访问路由就可以进行切换
  2. 这个多语言采用的是next-intl方案,需先安装next-intl
npm I -S next-intl
或
pnpm I -S next-intl

3. 在根目录中创建 message 文件夹用来放语言文件

image.png 语言简写列表

  1. 在 src 下新建 i18n.ts 文件,来配置我们的国际化逻辑。
// src/i18n.tsx
import {headers} from 'next/headers';
import {notFound} from 'next/navigation';
import {getRequestConfig} from 'next-intl/server';
import {locales} from './navigation';

export default getRequestConfig(async ({locale}) => {
  // Validate that the incoming `locale` parameter is valid
  if (!locales.includes(locale as any)) notFound();

  const now = headers().get('x-now');
  const timeZone = headers().get('x-time-zone') ?? 'Europe/Vienna';
  const messages = (await import(`../messages/${locale}.json`)).default;

  return {
    now: now ? new Date(now) : undefined,
    timeZone,
    messages,
    defaultTranslationValues: {
      globalString: 'Global string',
    //   highlight: (chunks) => <strong>{chunks}</strong>
    },
    formats: {
      dateTime: {
        medium: {
          dateStyle: 'medium',
          timeStyle: 'short',
          hour12: false
        }
      }
    },
    onError(error) {
      if (
        error.message ===
        (process.env.NODE_ENV === 'production'
          ? 'MISSING_MESSAGE'
          : 'MISSING_MESSAGE: Could not resolve `missing` in `Index`.')
      ) {
        // Do nothing, this error is triggered on purpose
      } else {
        console.error(JSON.stringify(error.message));
      }
    },
    getMessageFallback({key, namespace}) {
      return (
        '`getMessageFallback` called for ' +
        [namespace, key].filter((part) => part != null).join('.')
      );
    }
  };
});

这段逻辑全局配置了 国际化加载的路径,格式化数据的方式,时间等参数,当然还有更多的逻辑处理可以参考 next-intl 文档。

  1. 配置 navigation.tsx 内容定义了国际化的:
  • 默认语言和语言列表
  • 路由映射
  • 国际化路径前缀 这样我们后面在封装 国际化切换组件的收就会有很好的 ts提示
import {
    createLocalizedPathnamesNavigation,
    Pathnames
  } from 'next-intl/navigation';

  export const defaultLocale = 'en';

  export const locales = ['en', 'zh', 'fr', 'de', 'ar', 'ru'] as const;

  export const localePrefix =
    process.env.NEXT_PUBLIC_LOCALE_PREFIX === 'never' ? 'never' : 'as-needed';

  export const pathnames = {
    '/': '/',
    '/about': '/about',
    '/price': '/price',
  } satisfies Pathnames<typeof locales>;

  export const {Link, redirect, usePathname, useRouter} =
    createLocalizedPathnamesNavigation({
      locales,
      localePrefix,
      pathnames
    });

  1. 配置 next 国际化中间件 在 src 目录下新建 middleware.ts, 内容如下:
import createMiddleware from 'next-intl/middleware';
import {locales, pathnames, localePrefix, defaultLocale} from './navigation';

export default createMiddleware({
  defaultLocale,
  localePrefix,
  pathnames,
  locales,
});

export const config = {
  // Skip all paths that should not be internationalized
  matcher: ['/((?!_next|.*\\..*).*)']
};

  1. 在组件 / 页面中使用i18n
'use client';
import { useTranslations } from 'next-intl';

export default Page(){
  const t = useTranslations('global');
  
  return <div> { t('technological exchanges') } </div>
}
  1. 由于 next 项目支持客户端渲染和服务端渲染,所以使用 next-intl 的方式也是有区别的,如果我们在页面中出现 next-intl 相关的服务端渲染报错, 可以在页面同级添加 layout.tsx, 然后做如下封装:
import { NextIntlClientProvider, useMessages } from 'next-intl';

type Props = {
    children: React.ReactNode;
    params: {locale: string};
};

export default function LocaleLayout({children, params: { locale }}: Props) {
    // Receive messages provided in `i18n.ts`
    const messages = useMessages();
   
    return <NextIntlClientProvider locale={locale} messages={messages}>
                {children}
            </NextIntlClientProvider>
  }

然后运行项目就可以进行多语言切换了,默认语言为英文,对应的路径为根路径,切换中文后路径变为/zh或、/zh/xxx