样例参考:
https://github.dev/isaachinman/next-i18next/tree/master/examples/simple
翻译你的 NextJs 应用程序的最简单方法。
如果你在生产中使用 next-i18next,请考虑用你认为合适的金额赞助该软件包。
这是什么?
虽然 NextJs 直接提供了国际化的路由,但它并不处理任何翻译内容的管理,或实际的翻译功能本身。NextJs 所做的只是保持您的地域和 URL 的同步。
作为补充,next-i18next 提供了其余的功能 — 翻译内容的管理,以及翻译你的 React 组件的组件 / 钩子 — 同时完全支持 SSG/SSR、多命名空间、代码分割等。
next-i18next 在后台使用 i18next 和 react-i18next,而 next-i18next 的用户只需要将他们的翻译内容作为 JSON 文件,而不必担心其他问题。
这里有一个现场演示。这个演示应用程序是一个简单的例子 — 仅此而已。
为什么选择 next-i18next?
易于设置,易于使用:设置只需要几个步骤,配置也很简单。
没有其他要求:next-i18next 简化了你的 NextJs 应用程序的国际化,没有额外的依赖性。
生产就绪:next-i18next 支持将翻译和配置选项作为道具传入页面,并支持 SSG/SSR。
它是如何工作的?
你的 next-i18next.config.js 文件将为 next-i18next 提供配置。配置之后,appWithTranslation 允许我们通过钩子在我们的组件中使用 t(翻译)功能。
然后我们将 serverSideTranslation 添加到我们的页面级组件中的 getStaticProps 或 getServerSideProps(取决于你的情况)。
现在,我们的 NextJs 应用程序是完全可翻译的了。
设置
1. 安装
你还需要安装 react 和 next。
2. 翻译内容
默认情况下,next-i18next 希望你的翻译内容是这样组织的。
.
└── public
└── locales
├── en
| └── common.json
└── de
└── common.json
这个结构也可以在简单的例子中看到。
如果你想以自定义的方式来组织你的翻译 / 命名空间,你需要将修改后的 localePath 和 localeStructure 值传入初始化配置。
3. 项目设置
首先,在你的项目根部创建一个 next-i18next.config.js 文件。嵌套的 i18n 对象的语法直接来自 NextJs。
这将告诉 next-i18next 你的 defaultLocale 和其他语言是什么,以便它可以在服务器上预装翻译。
next-i18next.config.js
module.exports = {
i18n: {
defaultLocale: 'en',
locales: ['en', 'de'],
},
};
现在,创建或修改你的 next.config.js 文件,将 i18n 对象传入你的 next.config.js 文件,以启用本地化的 URL 路由。
[next.config.js](https://nextjs.org/docs/api-reference/next.config.js/introduction)
const { i18n } = require('./next-i18next.config');
module.exports = {
i18n,
};
next-i18next 输出了三个函数,你需要用它们来翻译你的项目。
appWithTranslation
这是一个包装你的_app 的 HOC。
import { appWithTranslation } from 'next-i18next';
const MyApp = ({ Component, pageProps }) => <Component {...pageProps} />;
export default appWithTranslation(MyApp);
appWithTranslation HOC 主要负责添加一个 I18nextProvider。
serverSideTranslations
这是一个异步函数,你需要通过 getStaticProps 或 getServerSideProps(取决于你的使用情况)在你的页面级组件中加入。
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
export async function getStaticProps({ locale }) {
return {
props: {
...(await serverSideTranslations(locale, ['common', 'footer'])),
// Will be passed to the page component as props
},
};
}
请注意,serverSideTranslations 必须从 next-i18next/serverSideTranslations 导入 — 这是一个单独的模块,包含 NodeJs 特定的代码。
另外,注意 serverSideTranslations 与 getInitialProps 不兼容,因为它只能在服务器环境下执行,而 getInitialProps 是在客户端在页面间导航时调用。
serverSideTranslations HOC 主要负责将翻译和配置选项作为道具传入页面 — 你需要将其添加到任何有翻译的页面。
useTranslation
这是一个钩子,你将实际用来做翻译本身。useTranslation 钩子来自 react-i18next,但也可以直接从 next-i18next 导入。
import { useTranslation } from 'next-i18next';
export const Footer = () => {
const { t } = useTranslation('footer');
return (
<footer>
<p>{t('description')}</p>
</footer>
);
};
4. 声明命名空间的依赖性
默认情况下,next-i18next 会在每次初始请求时将你的所有命名空间下发到客户端。对于内容较少的小型应用程序来说,这可能是一个合适的方法,但很多应用程序将受益于基于路由的命名空间的分割。
要做到这一点,你可以将每个页面所需的命名空间数组传递给 serverSideTranslations。你可以在 examples/simple/pages/index.js 中看到这种方法。传入一个所需命名空间的空数组将不发送任何命名空间。
注意:useTranslation 为你使用它的组件提供命名空间。然而,serverSideTranslations 为整个 React 树提供总的可用命名空间,属于页面级别。两者都是必需的。
5. 高级配置
传递其他配置选项
如果你需要修改更多的高级配置选项,你可以通过 next-i18next.config.js 传递它们。比如说。
const path = require('path');
module.exports = {
i18n: {
defaultLocale: 'en',
locales: ['en', 'de'],
},
localePath: path.resolve('./my/custom/path'),
};
不可序列化的配置
一些 i18next 插件(你可以传入 config.use)是不可序列化的,因为它们包含函数和其他 JavaScript 原语。
如果你的用例比较高级,你可能会遇到这种情况。你会看到 NextJs 抛出一个错误,比如。
Error: Error serializing `._nextI18Next.userConfig.use[0].process` returned from `getStaticProps` in "/my-page".
Reason: `function` cannot be serialized as JSON. Please only return JSON serializable data types.
要解决这个问题,你需要将 config.serializeConfig 设置为 false,并手动将你的配置传入 appWithTranslation。
import { appWithTranslation } from 'next-i18next';
import nextI18NextConfig from '../next-i18next.config.js';
const MyApp = ({ Component, pageProps }) => <Component {...pageProps} />;
export default appWithTranslation(MyApp, nextI18NextConfig);
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import nextI18NextConfig from '../next-i18next.config.js';
export const getStaticProps = async ({ locale }) => ({
props: {
...(await serverSideTranslations(
locale,
['common', 'footer'],
nextI18NextConfig
)),
},
});
在开发中重新加载资源
因为资源在服务器启动时被加载一次,所以在开发中对翻译 JSON 文件所做的任何修改都不会被加载,直到服务器重新启动。
在生产中,这往往不是一个问题,但在开发中,你可能希望看到你的翻译 JSON 文件的更新,而不必每次都重新启动你的开发服务器。要做到这一点,请将 reloadOnPrerender 配置选项设置为 true。
这个选项将在 serverSideTranslations 被调用时(在 getStaticProps 或 getServerSideProps 中)重新加载你的翻译。如果你在 getServerSideProps 中使用 serverSideTranslations,建议在生产环境中禁用 reloadOnPrerender,以避免在每次调用服务器时重新加载资源。
选项
Options
Key | Default value |
---|---|
defaultNS | ‘common’ |
localeExtension | ‘json’ |
localePath | ‘./public/locales’ |
localeStructure | ‘{{lng}}/{{ns}}’ |
reloadOnPrerender | false |
serializeConfig | true |
strictMode | true |
use (for plugins) | [] |
键值 | 默认值 |
---|---|
默认 NS |
'普通'。 |
localeExtension |
'json'。 |
localePath |
'./public/locales' |
定位结构(localeStructure |
'{{lng}}/{{ns}}' |
渲染时重新加载 |
错误 |
序列化配置(serializeConfig |
true |
严格模式 |
true |
使用 (对于插件) | [] |
所有其他的 i18next 选项也可以被传入。
客户端动态加载命名空间
在一些用例中,你可能想动态加载翻译文件,而不需要使用 serverSideTranslations。这对于你不希望拖慢页面的懒惰加载的组件特别有用。
这可以通过使用 addResourceBundle 轻松实现。
import { i18n } from 'next-i18next'
const Component = () => {
const { locale } = useRouter()
useEffect(() => {
i18n.addResourceBundle(locale, '<namespace name>')
}, [])
}
迁移到 V8
要从以前的版本迁移到第 8 版,请查看 v8 - 迁移指南。
注释
Vercel 和 Netlify
一些无服务器 PaaS 可能无法定位你的翻译路径,需要额外配置。如果你在使用 serverSideTranslations 时遇到文件系统问题,可以将 config.localePath 设置为使用 path.resolve。这里可以找到一个例子。
Docker
对于 Docker 部署,注意如果你使用 Next.js 文档中的 Dockerfile,不要忘记将 next.config.js 和 next-i18next.config.js 复制到 Docker 镜像中。
COPY --from=builder /app/next.config.js ./next.config.js
COPY --from=builder /app/next-i18next.config.js ./next-i18next.config.js
异步的 i18next 后端
如果你选择使用不同于内置 i18next-fs-backend 的 i18next 后端,你需要确保翻译资源在你调用 t 函数之前已经加载。由于 React suspense 还不支持 SSR,这可以通过 2 种不同的方式解决。
1)预装命名空间。
设置 ns 选项,就像这个例子中一样。这样做将确保所有翻译资源在初始化时被加载。
2)检查就绪标志。
如果你不能或不想提供 ns 数组,对 t 函数的调用将导致命名空间被即时加载。这意味着你需要通过检查 ready === true 或 props.tReady === true 来处理 “未就绪” 状态。不这样做会导致在翻译加载之前渲染你的翻译,这将导致 “保存丢失” 被调用,尽管翻译实际存在(只是还没有加载)。这可以通过 useTranslation 钩子或 withTranslation HOC 来实现。