目录

我的远程实习(一) | Nextjs 如何实现国际化😉

前言

前段时间 , 幸运的获得一个远程 offer , 主要业务是做 AI 出海应用 ,团队阵容十分强大:清华博士哥哥负责算法和产品 😱, 还有擅长架构和算法的 Full Stack Developer 阿伦🥰 , 他们真的很好 , 很贴心 ,即使是远程 ,我也能感受到团队合作的融入感 ~ 另一个的远程也是 :从业二十年的大厂大佬 , 平易近人 , 果然真正的大佬就是上善若水 ,没有任何傲慢与优越感 ~

这段时间我使用 Nextjs 框架做一些开发 ,由于是出海应用 , 所以国际化十分重要,在 Nextjs 中如何实现国际化? , 它的底层原理是什么 ?

本文涉及的代码参考与官网和公开资源 , 不涉及团队具体代码 , 哈哈 , 这点很重要🤡

什么是国际化 ?

我们在 header(头部导航栏)可以看到一个按钮供我们选择语言 ,这样理论上世界各国人民都能以本土语言使用网站(展示网站为 monica)

我们还要了解一些概念 :

  • 【国际化】就是i18n(Internationalizatio)
  • 【本地化】就是L10n(localization)
  • 【locale】它定义了软件运行时的语言环境,通过特定命名规则(language [_territory [.codeset]][@modifier])来确定语言、地域、字符集及修正值等信息 ,比如 :zh_CN.GB2312就表示中国地区的汉语

着重聊一下【locale】吧 ~

  • 在语言显示与交互方面,能让软件依据用户所在地区或设置偏好,将界面、菜单、提示等内容以对应语言呈现,如设为 "zh_CN",软件就以中文展示,提升跨国软件对不同用户的易用性。
  • 在日期和时间格式上,鉴于不同地区表示方式不同,locale 确保软件按当地习惯正确显示,方便用户在日程安排等功能中准确理解。
  • 涉及数字和货币格式时,由于各地数字分隔符、小数点使用及货币符号、格式有差异,locale 让金融、电商类软件能精准显示金额。
  • 在字符处理方面,因各语言字符排序规则不同,locale 为软件提供排序及文本处理规则,提升多语言文本操作的准确性。
  • 此外,软件可借助 locale 识别当地节假日,用于日历、考勤软件,还能适配特殊度量单位习惯,满足不同地区用户对功能的本地化需求 。

如何实现国际化 ?

方式很多 , 我使用的是 next-intl , 原因是 : 我不想破坏应用程序的目录结构和路由 ~

Gitee: gitee.com/luli1314520...

效果下动图

初始项目

初始化 nextjs 项目

代码结构

如何利用 next-intl 实现国际化

1. 配置支持的语言和默认语言

src/i18n/request.ts 文件中,定义了支持的语言列表、语言名称映射和默认语言:

ts 复制代码
export type Locale = 'en' | 'zh';

// 支持的语言列表
export const locales: Locale[] = ['en', 'zh'];

// 语言名称映射
export const localeNames: Record<Locale, string> = {
  en: 'English',
  zh: '中文'
};

// 默认语言
export const defaultLocale: Locale = 'zh'; 
2. 获取请求中的语言

src/i18n/request-locale.ts 文件中,实现了一个异步函数 requestLocale,用于从请求头中获取语言信息,如果获取失败则使用默认语言:

ts 复制代码
import {headers} from 'next/headers';
import {defaultLocale} from './request';

export async function requestLocale() {
  try {
    const headersList = await headers();
    return await headersList.get('X-NEXT-INTL-LOCALE') || defaultLocale;
  } catch {
    return defaultLocale;
  }
} 
3. 配置请求信息

src/i18n/index.ts 文件中,使用 getRequestConfig 函数配置请求信息,包括语言、消息文件、时区等:

ts 复制代码
import {getRequestConfig} from 'next-intl/server';
import {locales, defaultLocale} from './request';
import {requestLocale} from './request-locale';

export default getRequestConfig(async () => {
  const locale = await requestLocale();
  const validLocale = locales.includes(locale as any) ? locale : defaultLocale;
  
  return {
    locale: validLocale,
    messages: (await import(`../../messages/${validLocale}.json`)).default,
    timeZone: 'Asia/Shanghai',
    now: new Date()
  };
});
4. 配置中间件

src/middleware.ts 文件中,使用 next-intlcreateMiddleware 函数创建中间件,配置默认语言、支持的语言、语言前缀和语言检测等:

ts 复制代码
import createMiddleware from 'next-intl/middleware';
import {locales, defaultLocale} from './i18n/request';

// 配置需要匹配的路径
export const config = {
  // 匹配所有路径除了 api, _next/static, _next/image, favicon.ico 等
  matcher: ['/((?!api|_next|_vercel|.*\..*).*)', '/']
};

export default createMiddleware({
  // 默认语言
  defaultLocale,
  // 支持的语言
  locales,
  // 总是在URL中显示语言前缀
  localePrefix: 'always',
  // 禁用自动语言检测,使用URL中的语言参数
  localeDetection: false
});
5. 布局文件中提供国际化上下文

src/app/[locale]/layout.tsx 文件中,使用 NextIntlClientProvider 组件提供国际化上下文,包括语言和消息文件:

ts 复制代码
import '@/app/globals.css';
import { Inter } from 'next/font/google';
import { notFound } from 'next/navigation';
import { NextIntlClientProvider } from 'next-intl';
import { locales } from '@/i18n/request';
import { requestLocale } from '@/i18n/request-locale';

const inter = Inter({ subsets: ['latin'] });

export function generateStaticParams() {
  return locales.map((locale) => ({ locale }));
}

export default async function LocaleLayout({
  children,
  params
}: {
  children: React.ReactNode;
  params: { locale: string };
}) {
  const locale = await requestLocale() || params.locale;

  let messages;
  try {
    messages = (await import(`../../../messages/${locale}.json`)).default;
  } catch (error) {
    notFound();
  }

  return (
    <html lang={locale}>
      <body className={inter.className}>
        <NextIntlClientProvider 
          locale={locale} 
          messages={messages} 
          timeZone="Asia/Shanghai"
        >
          {children}
        </NextIntlClientProvider>
      </body>
    </html>
  );
}
6. 在页面中使用国际化

src/app/[locale]/page.tsx 文件中,使用 useTranslations 钩子获取翻译函数,并使用该函数进行文本翻译:

ts 复制代码
import { useTranslations } from 'next-intl';
import LanguageSwitcher from '@/components/LanguageSwitcher';

export default function Home() {
  const t = useTranslations('home');
  
  const features = [
    { key: 'routing', label: t('features.list.routing') },
    { key: 'seo', label: t('features.list.seo') },
    { key: 'performance', label: t('features.list.performance') },
    { key: 'typescript', label: t('features.list.typescript') }
  ];
  
  return (
    <main className="min-h-screen bg-gray-50">
      <div className="max-w-4xl mx-auto px-4 py-8">
        <div className="flex justify-between items-center mb-8">
          <h1 className="text-4xl font-bold text-gray-900">{t("title")}</h1>
          <LanguageSwitcher />
        </div>
        <p className="text-xl text-gray-600 mb-12">{t("description")}</p>
        <h2 className="text-2xl font-semibold text-gray-800 mb-6">{t("features.title")}</h2>
        <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
          {features.map(({ key, label }) => (
            <div key={key} className="bg-white p-6 rounded-lg shadow-sm hover:shadow-md transition-shadow">
              <p className="text-lg text-gray-700">{label}</p>
            </div>
          ))}
        </div>
      </div>
    </main>
  );
}
7. 切换语言组件

src/components/LanguageSwitcher.tsx 文件中,实现了一个语言切换组件:

ts 复制代码
'use client';

import { useLocale } from 'next-intl';
import { usePathname, useRouter } from 'next/navigation';
import { locales, localeNames, type Locale } from '@/i18n/request';

export default function LanguageSwitcher() {
  const locale = useLocale();
  const router = useRouter();
  const pathname = usePathname();

  const handleChange = (newLocale: string) => {
    // 替换URL中的语言代码
    const newPath = pathname.replace(`/${locale}`, `/${newLocale}`);
    router.push(newPath);
  };

  return (
    <select
      value={locale}
      onChange={(e) => handleChange(e.target.value)}
      className="bg-transparent border rounded px-2 py-1"
    >
      {locales.map((loc) => (
        <option key={loc} value={loc}>
          {localeNames[loc as Locale]}
        </option>
      ))}
    </select>
  );
}

当用户点击切换语言时,会触发 handleChange 函数,该函数会执行以下操作:

  1. 替换URL中的语言代码 :使用 pathname.replace 方法将当前URL中的语言代码替换为用户选择的新语言代码。
  2. 路由跳转 :使用 router.push 方法跳转到新的URL,此时页面会重新加载,并且 next-intl 会根据新的语言代码加载对应的消息文件,从而实现语言的切换。

通过以上步骤和代码实现了 next-intl 的国际化功能,并且在用户点击切换语言时能够正确切换页面的语言显示。

参考连接

  1. I18N
  2. wiki/区域设置
  3. Routing: Internationalization | Next.js
  4. juejin.cn/book/730785...
  5. juejin.cn/post/744672...
本文是转载文章,点击查看原文
如有侵权,请联系 xyy@jishuzhan.net 删除
相关推荐
小陈同学呦22 分钟前
聊聊vue中的keep-alive
前端·javascript·面试
喝拿铁写前端1 小时前
你以为你在封装组件,其实你在引入混乱
前端·架构
Json____1 小时前
智慧酒店企业站官网-前端静态网站模板【前端练习项目】
前端·网站模板·静态网站·企业站·智慧酒店网站
不爱说话郭德纲1 小时前
没有CICD,怎么自动化部署?
前端·javascript·vue.js
哔哩哔哩技术1 小时前
漫画产业加密技术探索与实践:抵御盗版的创新之路
前端
开心小老虎1 小时前
ThreeJs实现裸眼3D地球仪
前端·3d·threejs
大强的博客1 小时前
《Vue Router实战教程》21.扩展 RouterLink
前端·javascript·vue.js
@是你太难忘1 小时前
6.4案例:使用渲染函数渲染列表
前端·javascript·vue.js