实现Next.js多语言切换

前言

项目多语言国际化是一个非常重要的工作,特别是对于面向全球用户的应用程序来说。以下是一些看待项目多语言国际化的重要方面:

  1. 用户体验:通过多语言国际化,能够提升用户体验,让用户以自己熟悉的语言使用应用程序,使其更易于理解和操作。
  2. 全球市场:通过多语言支持,可以更好地进入全球市场,吸引更多不同语言背景的用户,扩大应用程序的受众群体。
  3. 文化尊重:多语言国际化也是对不同文化的尊重,能够让用户感受到应用程序对多样性和包容性的重视。
  4. 技术实现:在技术上,多语言国际化需要合适的工具和框架来管理不同语言的文本内容,并且需要有良好的设计架构和代码规范来支持国际化的需求。
  5. 维护和更新:项目多语言国际化需要考虑文本内容的维护和更新,确保在不同语言版本中保持一致性,因此需要建立合适的流程和工具来管理多语言内容。

那么本篇内容,会基于Next.js,配合使用第三方库如 react-i18nexti18next next-i18next 来管理多语言的内容。

快速开始

1.安装所需的依赖项

这是我在实际项目开发中,介绍在SSR版本Next.js的框架中如何使用多语言,结合使用next-i18n国际化库。

首先安装所需要的依赖:

js 复制代码
npm install react-i18next i18next next-i18next --save
//或者
yarn add react-i18next i18next next-i18next 

i18next 是提供所有翻译功能的核心,而react-i18next 则为与react 一起使用提供了一些额外的功能。

2.根目录下创建next-i18next.config.js

我在项目中配置的默认语言是中文,可切换语言是英文,可以根据实际情况,增加其他语言。 注意一下,官方是有例子的,有详细的配置。 地址:https://github.com/i18next/next-i18next

js 复制代码
/**
 * @type {import('next-i18next').UserConfig}
 */
module.exports = {
  debug: process.env.NODE_ENV === 'development',
  i18n: {
    defaultLocale: 'zh',//en 英文 zh 中文
    locales: ['en', 'zh'],//en 英文 zh 中文
  },
  localePath:
    typeof window === 'undefined'
      ? require('path').resolve('./public/locales')
      : '/locales',

  reloadOnPrerender: process.env.NODE_ENV === 'development',

以下是对于上面配置的解释(翻阅的资料):

  1. debug属性:设置i18next的调试模式。当环境变量NODE_ENVdevelopment时,启用调试模式,这会输出详细的日志信息以便开发者在开发阶段排查国际化相关问题。

  2. i18n对象:

    • defaultLocale属性:设置默认的语言环境为'zh',这意味着如果用户的浏览器没有特定的语言偏好或找不到匹配的语言包,将使用中文作为默认语言。
    • locales属性:列举了应用支持的所有语言环境列表,这里包括中文和英文。
  3. localePath属性:指定存储翻译文件的路径。根据运行环境不同进行动态配置:

    • 在服务器端(无浏览器环境)下,设定为项目根目录下的./public/locales路径。
    • 在客户端(存在浏览器环境)下,设定为URL路径/locales
  4. reloadOnPrerender属性:指示在预渲染期间(如Next.js的SSG或ISR)是否重新加载翻译资源。当环境变量NODE_ENVdevelopment时启用此选项,意味着在开发环境中,每当预渲染页面时都会重新加载最新的翻译数据。

3.创建文字文本文件

路径../next-i18next/examples/simple/public/locales/zh || en/common.json 创建两个或者多个作为切换的文本josn文件,主要一定要JSON文件,否则无法解析,并且JSON的键值对结构便于组织和维护配置项。

js 复制代码
//中文配置
{
  "language": "中文",
  "change-locale": "切换成 \"{{changeTo}}\"", //占位符"{{changeTo}}" 后续会解释
  "content": "您好!"
}
//英文配置
{
  "language": "English",
  "change-locale": "Change locale to \"{{changeTo}}\"",
  "content": "Hello!"
}

4.创建页面组件

官方示例中的_app.tsx_document.tsx参考:github.com/i18next/nex...

_app.tsx 复制代码
import type { AppProps } from 'next/app'
import { appWithTranslation } from 'next-i18next'

const MyApp = ({ Component, pageProps }: AppProps) => (
  <Component {...pageProps} />
)

export default appWithTranslation(MyApp /*, nextI18NextConfig */)
_document.tsx 复制代码
import Document, {
  Html,
  Head,
  Main,
  NextScript,
} from 'next/document'
import type { DocumentProps } from 'next/document'
import i18nextConfig from '../next-i18next.config'

type Props = DocumentProps & {
  // add custom document props
}

class MyDocument extends Document<Props> {
  render() {
    const currentLocale =
      this.props.__NEXT_DATA__.locale ??
      i18nextConfig.i18n.defaultLocale
    return (
      <Html lang={currentLocale}>
        <Head>
          <meta charSet="utf-8" />
          <link href="/app.css" rel="stylesheet" />
          // 可以添加相应的全局样式
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    )
  }
}

export default MyDocument
index.tsx 复制代码
import Link from 'next/link'
import { useRouter } from 'next/router'
import type { InferGetStaticPropsType } from 'next'
import { useTranslation, Trans } from 'next-i18next'
import { Header } from '../components/Header'

const Page = (_props: InferGetStaticPropsType<typeof getServerSideProps>) => {
  const router = useRouter();
  const { t } = useTranslation('common');
  // 如果有多个语法 => const { t } = useTranslation(['common', 'page'])
  const changeTo = router.locale === 'zh' ? 'en' : 'zh';

  return (
    <>
      <main>
        <div style={{ display: 'inline-flex', width: '90%' }}>
          <div style={{ width: '100%' }}>
            <h3 style={{ minHeight: 70 }}>
              {t('to-second-page')}
              <Trans i18nKey="to-second-page">
                您好!
              </Trans>
              //按照需求可以选择使用什么方法作为翻译的方式(t or <Trans/>)
            </h3>
          </div>
        </div>
        <hr style={{ marginTop: 20, width: '90%' }} />
        <div>
          <Link href="/" locale={changeTo}>
            <button>{t('change-locale', { changeTo })}</button>
          </Link>
          <Link href="">
            <button type="button">{t('to-second-page')}</button>
          </Link>
        </div>
      </main>
    </>
  )
}

export default Page;

<Link> 组件来自 Next.js 库,它用于创建一个到其他页面的链接。这段代码的意义在于提供两个按钮,分别用于切换语言环境并跳转至对应语言的首页以及跳转到应用中的第二个页面,并确保所有显示的文本都经过了i18n的翻译处理。

以下是对于next-i18next库使用的解释:

  1. useTranslation 可以理解为一个Hook, 是 react-i18next 库中的一个函数,用于在React应用中进行国际化(i18n)处理。目前我知道的,它返回的对象包含两个属性t、i18n。
  2. t: 这是一个翻译函数,通常用于将指定的键转换为对应语言的字符串。t 方法可以接受多个参数,但最常见的是一个字符串参数,代表要翻译的键。此外,还可以传递一个选项对象(例如,用于替换占位符),或者一个数组作为第一个参数以支持复数形式。
  3. i18n: 这个对象提供了对i18next实例的访问,允许开发者直接操作国际化设置、切换语言等更高级的操作。这个对象的方法和属性不在此处详细列举,但它不直接与t方法的参数数量相关。
  4. <Trans> 组件是一个用于翻译和本地化的组件。这个组件允许你定义需要被翻译的字符串,并且可以包含占位符或其他复杂的结构,比如复数形式、性别等。
  5. i18nKey 属性:这是指定了要翻译的键值,对应的是你的翻译资源文件(如JSON)中的一个键名。在组件是默认情况下当找不到翻译或初始化标签内的文本内容 "您好!"时显示的内容,它也可以作为开发过程中的参考文本。

6.使用getStaticProps或者getServerSideProps函数

我个人喜欢把方法抽出去,我的路径是:../Hooks/serverSideProps.ts,我使用的是getServerSideProps,其类型为GetServerSideProps。

js 复制代码
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';

export const getServerSideProps: GetServerSideProps<Props> = async ({
  locale,
  // 如果locale参数不存在或为空,则默认使用'zh'作为语言环境
}) => ({
// 返回一个对象,其中props属性包含了从i18n加载得到的翻译数据
  props: {
    ...(await serverSideTranslations(locale ?? 'zh', [
    // 如果locale参数不存在或为空,则默认使用'zh'作为语言环境
      'common',// 你的语言文本JOSN文件名,要几个写几个
      // 加载名为'common'的命名空间下的翻译
    ])),
  },
})

这段代码 locale 参数是在 getServerSidePropsgetStaticProps 函数的上下文对象 (context) 中提供的,框架会自动解析并提供当前请求的语言环境信息,用于H5手动切换语言的场景。

js 复制代码
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';

export const getServerSideProps = async context => {
  const cookies = context.req.headers.cookie; 
  // 从请求头中获取所有的Cookie信息,其中包含:
  // context.params: 包含解析自动态路由的参数
  // context.query: 包含URL查询参数对象
  // context.req: Node.js原始请求对象
  try {
    const cookieArray = cookies.split('; ');
    let formattedCookie: any = {};
    cookieArray.forEach(cookie => {
      const [name, value] = cookie.split('=');
      formattedCookie[name] = value;
    });
    return {
      props: {
        cookies: {
          ...formattedCookie,
        },
        ...(await serverSideTranslations(formattedCookie?.LOCALE ?? 'zh', [
        // 如果formattedCookie.LOCALE参数不存在或为空,则默认使用'zh'作为语言环境
          'common',// 你的语言文本JOSN文件名,要几个写几个
          // 加载名为'common'的命名空间下的翻译
        ])),
      },
    };
  } catch (error) {
    return {
      props: {
        cookies: {},
      },
    };
  }
};

而这段代码是通过安卓在当前路由中,种植Cookies,H5使用getServerSideProps获取,并判断当前的语言,如果未获取默认就是中文,适合用于跨端切换语言的场景。

  1. getServerSideProps:

    • 用于服务器端渲染(Server-side Rendering, SSR)场景。
    • 每当客户端请求页面时,在服务器端实时执行该函数。
    • 返回的数据会在每次请求时动态生成,确保了每次用户访问都能获取最新的数据。
    • 不利于SEO优化,因为搜索引擎爬虫看到的内容可能会因为请求时间不同而不同。
  2. getStaticProps:

    • 用于静态生成(Static Site Generation, SSG)场景。
    • 在构建时执行,为页面生成静态HTML文件及相关的JSON数据。
    • 返回的数据会被注入到对应的页面组件中,并且这些数据会随着页面一起被预渲染并部署到服务器上。
    • 当客户端请求页面时,服务端直接返回预先生成好的HTML,提高加载速度和SEO表现。
  3. 如果你的内容相对静态并且不需要实时更新,或者你要是希望提高SEO效果和首次加载速度,可以选择 getStaticProps。但是如果你需要在每次用户请求时都获取最新数据,则应选用 getServerSideProps

如果不出意外的话,运行项目,打开你的页面,就能根据路径查看到不同语言页面了。

7.总结

我在项目中对多语言的支持进行了以下改造:

  1. 引入了 react-i18next i18next next-i18next ,针对Next.js应用优化的i18n解决方案。通过引入 useTranslationserverSideTranslations 函数,实现了动态翻译功能和服务器端生成翻译资源。
  2. 在组件内部使用 useTranslation('common') 钩子来获取当前语言环境下的翻译函数t,该函数用于根据国际化配置文件翻译字符串。
  3. 页面内容中的文本均采用 t 函数进行翻译,确保页面上的所有可翻译文本都可根据用户选择的语言自动转换。
  4. 使用 <Trans> 组件处理更复杂的翻译需求,比如包含占位符的句子,它可以解析并替换其中的变量。
  5. 实现了语言切换功能:通过 router.locale 来判断当前语言环境,并据此设置 changeTo 变量为另一种语言。然后,在 <Link> 组件中利用 locale 属性实现路由级别的语言切换。
  6. getServerSideProps 函数中,通过调用 serverSideTranslations 获取指定语言环境下需要的翻译资源,这些资源会被注入到组件的props中,在服务器端生成时就包含了对应的翻译文本,从而提高了首屏加载速度和SEO性能。

总之,以上内容展示了如何在一个Next.js应用中集成实现多语言支持、动态翻译以及语言切换等功能。 (感谢您的阅读,希望对您有所帮助!)

相关推荐
开心工作室_kaic10 分钟前
ssm068海鲜自助餐厅系统+vue(论文+源码)_kaic
前端·javascript·vue.js
有梦想的刺儿29 分钟前
webWorker基本用法
前端·javascript·vue.js
cy玩具1 小时前
点击评论详情,跳到评论页面,携带对象参数写法:
前端
qq_390161772 小时前
防抖函数--应用场景及示例
前端·javascript
John.liu_Test2 小时前
js下载excel示例demo
前端·javascript·excel
Yaml42 小时前
智能化健身房管理:Spring Boot与Vue的创新解决方案
前端·spring boot·后端·mysql·vue·健身房管理
PleaSure乐事2 小时前
【React.js】AntDesignPro左侧菜单栏栏目名称不显示的解决方案
前端·javascript·react.js·前端框架·webstorm·antdesignpro
哟哟耶耶2 小时前
js-将JavaScript对象或值转换为JSON字符串 JSON.stringify(this.SelectDataListCourse)
前端·javascript·json
getaxiosluo2 小时前
react jsx基本语法,脚手架,父子传参,refs等详解
前端·vue.js·react.js·前端框架·hook·jsx
理想不理想v2 小时前
vue种ref跟reactive的区别?
前端·javascript·vue.js·webpack·前端框架·node.js·ecmascript