i18next
i18next 是一套用于 Web/Node 端的国际化(i18n)框架,帮助应用把界面文案与代码逻辑解耦,实现多语言翻译、插值、复数、语境等能力的统一管理。它以“资源字典 + 命名空间(namespace)+ key”为核心组织方式,通过 `t('key')` 在运行时根据当前语言与回退策略(fallback)返回对应文案,并支持变量插值(如 `{{name}}`)、复数规则与日期/数字等本地化配合。i18next 生态完善:可结合 `react-i18next` 等适配不同框架,使用后端/浏览器语言检测、按需加载(lazy load)与缓存提升性能,也便于在大型项目中做模块化拆分与动态切换语言。总体而言,i18next 提供了可扩展、工程化的 i18n 基础设施,适合从小型应用到多团队大仓库的多语言需求。

查看更多相关内容
如何在 React 项目中使用 react-i18next?## react-i18next 简介
react-i18next 是 i18next 的 React 专用绑定库,提供了 React Hooks 和组件,使国际化在 React 应用中更加便捷。
## 安装
```bash
npm install react-i18next i18next
# 或
yarn add react-i18next i18next
```
## 基本配置
### 初始化
```javascript
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import Backend from 'i18next-http-backend';
import LanguageDetector from 'i18next-browser-languagedetector';
i18n
.use(Backend) // 加载翻译资源
.use(LanguageDetector) // 检测用户语言
.use(initReactI18next) // 绑定 react-i18next
.init({
fallbackLng: 'en',
debug: true,
interpolation: {
escapeValue: false // React 已经处理了 XSS
}
});
export default i18n;
```
### 在应用入口引入
```javascript
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import './i18n'; // 引入 i18n 配置
ReactDOM.render(<App />, document.getElementById('root'));
```
## 使用 useTranslation Hook
### 基本用法
```javascript
import React from 'react';
import { useTranslation } from 'react-i18next';
function Welcome() {
const { t } = useTranslation();
return <h1>{t('welcome')}</h1>;
}
```
### 指定命名空间
```javascript
function MyComponent() {
const { t } = useTranslation('common');
return <button>{t('save')}</button>;
}
```
### 多个命名空间
```javascript
function MyComponent() {
const { t } = useTranslation(['common', 'errors']);
return (
<div>
<button>{t('common:save')}</button>
<p>{t('errors:notFound')}</p>
</div>
);
}
```
## 使用 Trans 组件
### 基本用法
```javascript
import { Trans } from 'react-i18next';
function MyComponent() {
return (
<Trans i18nKey="user.profile">
Welcome <strong>{{name}}</strong> to your profile
</Trans>
);
}
// 翻译资源
// "user.profile": "Welcome <1>{{name}}</1> to your profile"
```
### 嵌套组件
```javascript
<Trans i18nKey="description">
Click <Link to="/about">here</Link> to learn more
</Trans>
```
## 使用 withTranslation HOC
```javascript
import { withTranslation } from 'react-i18next';
class MyComponent extends React.Component {
render() {
const { t } = this.props;
return <h1>{t('title')}</h1>;
}
}
export default withTranslation()(MyComponent);
```
## 使用 I18nextProvider
```javascript
import { I18nextProvider } from 'react-i18next';
import i18n from './i18n';
function App() {
return (
<I18nextProvider i18n={i18n}>
<MyComponent />
</I18nextProvider>
);
}
```
## 语言切换
### 创建语言切换器
```javascript
import { useTranslation } from 'react-i18next';
function LanguageSwitcher() {
const { i18n } = useTranslation();
const changeLanguage = (lng) => {
i18n.changeLanguage(lng);
};
return (
<div>
<button onClick={() => changeLanguage('en')}>English</button>
<button onClick={() => changeLanguage('zh')}>中文</button>
</div>
);
}
```
### 监听语言变化
```javascript
function MyComponent() {
const { t, i18n } = useTranslation();
const [currentLanguage, setCurrentLanguage] = useState(i18n.language);
useEffect(() => {
const handleLanguageChange = (lng) => {
setCurrentLanguage(lng);
};
i18n.on('languageChanged', handleLanguageChange);
return () => {
i18n.off('languageChanged', handleLanguageChange);
};
}, [i18n]);
return <p>Current language: {currentLanguage}</p>;
}
```
## 延迟加载命名空间
```javascript
function LazyComponent() {
const { t, ready } = useTranslation('lazyNamespace', { useSuspense: false });
if (!ready) {
return <div>Loading...</div>;
}
return <p>{t('content')}</p>;
}
```
## 最佳实践
1. **按需加载**: 使用命名空间和延迟加载优化性能
2. **类型安全**: TypeScript 项目使用类型定义
3. **错误处理**: 处理缺失的翻译
4. **测试**: 编写国际化相关的单元测试
5. **代码分割**: 将翻译资源与应用代码分离
服务端 · 2月18日 22:07
如何为 i18next 编写测试?## 基本测试
### 测试翻译功能
```javascript
import i18next from 'i18next';
describe('i18next translations', () => {
beforeEach(() => {
i18next.init({
lng: 'en',
resources: {
en: {
translation: {
welcome: 'Welcome',
greeting: 'Hello {{name}}'
}
}
}
});
});
test('should translate simple key', () => {
expect(i18next.t('welcome')).toBe('Welcome');
});
test('should translate with interpolation', () => {
expect(i18next.t('greeting', { name: 'John' })).toBe('Hello John');
});
});
```
## React 组件测试
### 测试使用 useTranslation 的组件
```javascript
import { render, screen } from '@testing-library/react';
import { useTranslation } from 'react-i18next';
import { I18nextProvider } from 'react-i18next';
import i18next from 'i18next';
function Welcome() {
const { t } = useTranslation();
return <h1>{t('welcome')}</h1>;
}
describe('Welcome component', () => {
const renderWithI18n = (component) => {
return render(
<I18nextProvider i18n={i18next}>
{component}
</I18nextProvider>
);
};
beforeEach(() => {
i18next.init({
lng: 'en',
resources: {
en: {
translation: {
welcome: 'Welcome'
}
}
}
});
});
test('should display welcome message', () => {
renderWithI18n(<Welcome />);
expect(screen.getByText('Welcome')).toBeInTheDocument();
});
});
```
### 测试语言切换
```javascript
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { useTranslation } from 'react-i18next';
import { I18nextProvider } from 'react-i18next';
import i18next from 'i18next';
function LanguageSwitcher() {
const { t, i18n } = useTranslation();
return (
<div>
<p>{t('currentLang')}</p>
<button onClick={() => i18n.changeLanguage('zh')}>中文</button>
<button onClick={() => i18n.changeLanguage('en')}>English</button>
</div>
);
}
describe('LanguageSwitcher', () => {
beforeEach(() => {
i18next.init({
lng: 'en',
resources: {
en: {
translation: {
currentLang: 'English'
}
},
zh: {
translation: {
currentLang: '中文'
}
}
}
});
});
test('should switch language', async () => {
render(
<I18nextProvider i18n={i18next}>
<LanguageSwitcher />
</I18nextProvider>
);
expect(screen.getByText('English')).toBeInTheDocument();
fireEvent.click(screen.getByText('中文'));
await waitFor(() => {
expect(screen.getByText('中文')).toBeInTheDocument();
});
});
});
```
## 测试 Trans 组件
```javascript
import { render, screen } from '@testing-library/react';
import { Trans } from 'react-i18next';
import { I18nextProvider } from 'react-i18next';
import i18next from 'i18next';
function MyComponent() {
return (
<Trans i18nKey="user.greeting">
Welcome <strong>{{name}}</strong>
</Trans>
);
}
describe('Trans component', () => {
beforeEach(() => {
i18next.init({
lng: 'en',
resources: {
en: {
translation: {
'user.greeting': 'Welcome <1>{{name}}</1>'
}
}
}
});
});
test('should render translated content with interpolation', () => {
render(
<I18nextProvider i18n={i18next}>
<MyComponent />
</I18nextProvider>
);
expect(screen.getByText(/Welcome/i)).toBeInTheDocument();
});
});
```
## Mock i18next
### 创建测试工具函数
```javascript
// test-utils/i18n.js
import { initReactI18next } from 'react-i18next';
import i18next from 'i18next';
export const createI18nInstance = (resources = {}) => {
const instance = i18next.createInstance();
instance.use(initReactI18next).init({
lng: 'en',
resources: {
en: {
translation: resources
}
}
});
return instance;
};
export const renderWithI18n = (component, resources = {}) => {
const i18n = createI18nInstance(resources);
return render(
<I18nextProvider i18n={i18n}>
{component}
</I18nextProvider>
);
};
```
### 使用测试工具
```javascript
import { renderWithI18n } from './test-utils/i18n';
test('should render with mocked translations', () => {
renderWithI18n(<MyComponent />, {
welcome: 'Welcome',
goodbye: 'Goodbye'
});
expect(screen.getByText('Welcome')).toBeInTheDocument();
});
```
## 测试延迟加载
```javascript
import { render, screen, waitFor } from '@testing-library/react';
import { useTranslation } from 'react-i18next';
import { I18nextProvider } from 'react-i18next';
import i18next from 'i18next';
function LazyComponent() {
const { t, ready } = useTranslation('lazy', { useSuspense: false });
if (!ready) {
return <div>Loading...</div>;
}
return <p>{t('content')}</p>;
}
describe('Lazy loading', () => {
beforeEach(() => {
i18next.init({
lng: 'en',
resources: {
en: {
translation: {}
}
}
});
});
test('should show loading state initially', () => {
render(
<I18nextProvider i18n={i18next}>
<LazyComponent />
</I18nextProvider>
);
expect(screen.getByText('Loading...')).toBeInTheDocument();
});
test('should load namespace and show content', async () => {
render(
<I18nextProvider i18n={i18next}>
<LazyComponent />
</I18nextProvider>
);
i18next.addResourceBundle('en', 'lazy', { content: 'Loaded content' });
await waitFor(() => {
expect(screen.getByText('Loaded content')).toBeInTheDocument();
});
});
});
```
## 测试缺失翻译
```javascript
describe('Missing translations', () => {
beforeEach(() => {
i18next.init({
lng: 'en',
fallbackLng: 'en',
saveMissing: true,
missingKeyHandler: (lng, ns, key) => {
console.warn(`Missing translation: ${lng}.${ns}.${key}`);
},
resources: {
en: {
translation: {
existing: 'Existing translation'
}
}
}
});
});
test('should return key when translation is missing', () => {
const result = i18next.t('nonexistent');
expect(result).toBe('nonexistent');
});
test('should use fallback translation', () => {
i18next.addResourceBundle('en', 'translation', { fallback: 'Fallback' });
const result = i18next.t('fallback');
expect(result).toBe('Fallback');
});
});
```
## 集成测试
```javascript
describe('i18next integration tests', () => {
test('should handle language change across components', async () => {
const { rerender } = render(
<I18nextProvider i18n={i18next}>
<App />
</I18nextProvider>
);
expect(screen.getByText('English')).toBeInTheDocument();
await i18next.changeLanguage('zh');
rerender(
<I18nextProvider i18n={i18next}>
<App />
</I18nextProvider>
);
expect(screen.getByText('中文')).toBeInTheDocument();
});
});
```
## 最佳实践
1. **隔离测试**: 每个测试应该独立运行,不依赖其他测试
2. **Mock 资源**: 使用 mock 的翻译资源,避免依赖实际文件
3. **测试工具**: 创建可复用的测试工具函数
4. **异步测试**: 正确处理异步操作,如语言切换和延迟加载
5. **覆盖边界**: 测试缺失翻译、错误情况等边界条件
6. **快照测试**: 对翻译组件使用快照测试确保一致性
7. **性能测试**: 测试大量翻译时的性能表现
服务端 · 2月18日 22:07
如何在项目中初始化和配置 i18next?## 基本初始化
i18next 的初始化非常简单,使用 `i18next.init()` 方法进行配置:
```javascript
import i18next from 'i18next';
i18next.init({
lng: 'en', // 默认语言
debug: true, // 开发模式下开启调试
resources: {
en: {
translation: {
"key": "Hello World"
}
},
zh: {
translation: {
"key": "你好世界"
}
}
}
}, (err, t) => {
if (err) {
console.error('i18next 初始化失败:', err);
return;
}
// 初始化完成后的回调
console.log(t('key')); // 输出: Hello World
});
```
## 关键配置选项
### 语言相关
- **lng**: 设置默认语言(如 'en', 'zh', 'fr')
- **fallbackLng**: 当翻译不存在时的回退语言
- **supportedLngs**: 支持的语言列表
```javascript
i18next.init({
lng: 'zh',
fallbackLng: 'en',
supportedLngs: ['en', 'zh', 'fr', 'de']
});
```
### 资源相关
- **resources**: 直接定义翻译资源
- **ns**: 默认命名空间
- **defaultNS**: 默认使用的命名空间
```javascript
i18next.init({
resources: {
en: {
translation: {
"welcome": "Welcome"
},
common: {
"save": "Save",
"cancel": "Cancel"
}
}
},
ns: ['translation', 'common'],
defaultNS: 'translation'
});
```
### 插值相关
- **interpolation**: 配置插值行为
```javascript
i18next.init({
interpolation: {
escapeValue: false, // 不转义 HTML
format: function(value, format, lng) {
if (format === 'uppercase') return value.toUpperCase();
return value;
}
}
});
```
### 检测相关
- **detection**: 语言检测配置
```javascript
i18next.init({
detection: {
order: ['querystring', 'cookie', 'localStorage', 'navigator'],
caches: ['localStorage', 'cookie'],
lookupQuerystring: 'lng'
}
});
```
## 异步初始化
i18next.init() 返回一个 Promise,可以使用 async/await:
```javascript
async function initI18n() {
try {
await i18next.init({
lng: 'zh',
resources: {
zh: {
translation: {
"hello": "你好"
}
}
}
});
console.log('i18next 初始化成功');
} catch (error) {
console.error('初始化失败:', error);
}
}
```
## 最佳实践
1. **分离配置**: 将 i18next 配置单独放在一个文件中
2. **环境区分**: 开发和生产环境使用不同配置
3. **错误处理**: 始终处理初始化错误
4. **延迟加载**: 大型应用使用延迟加载提高性能
5. **类型安全**: TypeScript 项目使用类型定义
服务端 · 2月18日 22:07
如何在 TypeScript 项目中使用 i18next?## 基本类型定义
### 安装类型定义
```bash
npm install --save-dev @types/i18next
# 对于 react-i18next
npm install --save-dev @types/react-i18next
```
### 定义翻译资源类型
```typescript
interface TranslationResources {
en: {
translation: EnTranslation;
};
zh: {
translation: ZhTranslation;
};
}
interface EnTranslation {
welcome: string;
greeting: string;
user: {
name: string;
profile: string;
};
}
interface ZhTranslation {
welcome: string;
greeting: string;
user: {
name: string;
profile: string;
};
}
```
## 使用 useTranslation Hook
### 基本用法
```typescript
import { useTranslation } from 'react-i18next';
function MyComponent() {
const { t } = useTranslation();
return <h1>{t('welcome')}</h1>;
}
```
### 指定命名空间
```typescript
function MyComponent() {
const { t } = useTranslation('common');
return <button>{t('save')}</button>;
}
```
### 类型安全的翻译键
```typescript
// 定义翻译键类型
type TranslationKeys = 'welcome' | 'greeting' | 'user.name';
function useTypedTranslation() {
const { t } = useTranslation();
return {
t: (key: TranslationKeys, options?: any) => t(key, options)
};
}
function MyComponent() {
const { t } = useTypedTranslation();
return <h1>{t('welcome')}</h1>;
}
```
## 创建类型安全的翻译函数
### 使用泛型
```typescript
import i18next from 'i18next';
type TranslationFunction<T> = (key: keyof T, options?: any) => string;
function createTypedTranslation<T>(ns: string): TranslationFunction<T> {
return (key: keyof T, options?: any) => {
return i18next.t(`${ns}:${String(key)}`, options);
};
}
// 使用
interface CommonTranslations {
save: string;
cancel: string;
delete: string;
}
const tCommon = createTypedTranslation<CommonTranslations>('common');
function MyComponent() {
return (
<div>
<button>{tCommon('save')}</button>
<button>{tCommon('cancel')}</button>
</div>
);
}
```
## 使用 i18next-resources-to-ts
### 生成类型定义
```bash
npm install --save-dev i18next-resources-to-ts
```
```javascript
// scripts/generate-types.js
const { generateTypes } = require('i18next-resources-to-ts');
const fs = require('fs');
generateTypes('./locales', {
indent: 2,
sort: true,
lineEnding: 'lf'
}).then(types => {
fs.writeFileSync('./src/types/i18n.d.ts', types);
console.log('Types generated successfully');
});
```
### 在 package.json 中添加脚本
```json
{
"scripts": {
"generate:i18n-types": "node scripts/generate-types.js"
}
}
```
## 插值类型安全
```typescript
interface GreetingOptions {
name: string;
title?: string;
}
function useGreeting() {
const { t } = useTranslation();
return (options: GreetingOptions) => {
return t('greeting', options);
};
}
function MyComponent() {
const greet = useGreeting();
return (
<div>
{greet({ name: 'John' })}
{greet({ name: 'Jane', title: 'Dr.' })}
</div>
);
}
```
## 复数处理类型
```typescript
interface PluralOptions {
count: number;
item: string;
}
function usePlural() {
const { t } = useTranslation();
return (options: PluralOptions) => {
return t('item_count', options);
};
}
function MyComponent() {
const plural = usePlural();
return (
<div>
{plural({ count: 1, item: 'apple' })}
{plural({ count: 5, item: 'apple' })}
</div>
);
}
```
## 上下文类型
```typescript
type Context = 'male' | 'female' | 'other';
interface ContextOptions {
context: Context;
name: string;
}
function useContextTranslation() {
const { t } = useTranslation();
return (options: ContextOptions) => {
return t('friend', options);
};
}
function MyComponent() {
const tFriend = useContextTranslation();
return (
<div>
{tFriend({ context: 'male', name: 'John' })}
{tFriend({ context: 'female', name: 'Jane' })}
</div>
);
}
```
## 命名空间类型
```typescript
interface Namespaces {
common: CommonTranslations;
errors: ErrorTranslations;
user: UserTranslations;
}
function useNamespacedTranslation<K extends keyof Namespaces>(
namespace: K
) {
const { t } = useTranslation(namespace);
return {
t: (key: keyof Namespaces[K], options?: any) => t(key, options)
};
}
function MyComponent() {
const { t: tCommon } = useNamespacedTranslation('common');
const { t: tErrors } = useNamespacedTranslation('errors');
return (
<div>
<button>{tCommon('save')}</button>
<p>{tErrors('notFound')}</p>
</div>
);
}
```
## Trans 组件类型
```typescript
import { Trans } from 'react-i18next';
interface TransProps {
i18nKey: string;
values?: Record<string, string | number>;
components?: Record<string, React.ReactNode>;
}
function TypedTrans({ i18nKey, values, components }: TransProps) {
return (
<Trans
i18nKey={i18nKey}
values={values}
components={components}
/>
);
}
function MyComponent() {
return (
<TypedTrans
i18nKey="user.greeting"
values={{ name: 'John' }}
components={{ strong: <strong /> }}
/>
);
}
```
## 最佳实践
1. **生成类型**: 使用工具自动生成翻译资源类型
2. **严格模式**: 启用 TypeScript 严格模式
3. **命名空间**: 使用命名空间组织翻译
4. **类型守卫**: 使用类型守卫确保类型安全
5. **持续集成**: 在 CI/CD 中生成和检查类型
6. **文档**: 记录类型定义和使用方法
7. **测试**: 编写类型测试确保类型正确性
服务端 · 2月18日 22:07
i18next 中如何使用插值、复数和上下文功能?## 基本翻译
使用 `t()` 函数进行翻译:
```javascript
// 简单翻译
t('welcome'); // "Welcome"
// 嵌套键
t('header.title'); // "Dashboard"
```
## 插值功能
### 变量插值
在翻译文本中使用 `{{variable}}` 语法:
```javascript
// 翻译资源
{
"greeting": "Hello {{name}}",
"userCount": "Total users: {{count}}"
}
// 使用
t('greeting', { name: 'John' }); // "Hello John"
t('userCount', { count: 100 }); // "Total users: 100"
```
### 数组插值
```javascript
{
"list": "Items: {{items}}"
}
t('list', { items: ['apple', 'banana'].join(', ') });
```
## 复数处理
i18next 内置对复数的支持:
```javascript
// 翻译资源
{
"item": "one item",
"item_plural": "{{count}} items"
}
// 使用
t('item', { count: 1 }); // "one item"
t('item', { count: 5 }); // "5 items"
```
### 自定义复数规则
```javascript
i18next.init({
lng: 'ru',
resources: {
ru: {
translation: {
"item_one": "один предмет",
"item_few": "{{count}} предмета",
"item_many": "{{count}} предметов",
"item_other": "{{count}} предмет"
}
}
}
});
```
## 上下文处理
根据上下文显示不同的翻译:
```javascript
// 翻译资源
{
"friend": "A friend",
"friend_male": "A boyfriend",
"friend_female": "A girlfriend"
}
// 使用
t('friend', { context: 'male' }); // "A boyfriend"
t('friend', { context: 'female' }); // "A girlfriend"
```
## 命名空间
### 使用命名空间
```javascript
i18next.init({
ns: ['common', 'errors'],
defaultNS: 'common',
resources: {
en: {
common: {
"save": "Save"
},
errors: {
"notFound": "Not found"
}
}
}
});
// 使用
t('save'); // 使用默认命名空间
t('errors:notFound'); // 指定命名空间
```
### 动态加载命名空间
```javascript
i18next.loadNamespaces(['admin', 'settings'], () => {
// 命名空间加载完成
});
```
## 格式化
### 自定义格式化函数
```javascript
i18next.init({
interpolation: {
format: function(value, format, lng) {
if (format === 'uppercase') {
return value.toUpperCase();
}
if (format === 'currency') {
return new Intl.NumberFormat(lng, {
style: 'currency',
currency: 'USD'
}).format(value);
}
return value;
}
}
});
// 使用
t('price', { price: 100, formatParams: { price: { format: 'currency' } } });
```
## 嵌套翻译
```javascript
// 翻译资源
{
"user": {
"profile": {
"name": "Name: {{name}}"
}
}
}
// 使用
t('user.profile.name', { name: 'John' });
```
## 缺失翻译处理
```javascript
i18next.init({
saveMissing: true,
missingKeyHandler: (lng, ns, key) => {
console.log(`Missing translation: ${lng}.${ns}.${key}`);
}
});
```
## 性能优化
```javascript
// 批量翻译
const keys = ['welcome', 'goodbye', 'save'];
const translations = keys.map(key => t(key));
// 缓存翻译结果
const cachedTranslations = new Map();
function getCachedTranslation(key, options) {
const cacheKey = `${key}_${JSON.stringify(options)}`;
if (!cachedTranslations.has(cacheKey)) {
cachedTranslations.set(cacheKey, t(key, options));
}
return cachedTranslations.get(cacheKey);
}
```
服务端 · 2月18日 22:06
i18next 常见问题及解决方案有哪些?## 常见问题
### 1. 翻译不显示
**问题**: 调用 `t()` 函数返回键名而不是翻译文本
**原因**:
- 翻译资源未正确加载
- 命名空间配置错误
- 语言代码不匹配
**解决方案**:
```javascript
// 检查翻译资源是否加载
console.log(i18next.store.data);
// 确保语言代码匹配
i18next.init({
lng: 'en', // 确保与资源中的键匹配
resources: {
en: {
translation: {
welcome: 'Welcome'
}
}
}
});
// 检查命名空间
i18next.init({
ns: ['translation', 'common'],
defaultNS: 'translation'
});
```
### 2. 语言切换不生效
**问题**: 调用 `changeLanguage()` 后翻译没有更新
**原因**:
- 组件没有监听语言变化
- 翻译资源未加载
- React 组件没有重新渲染
**解决方案**:
```javascript
// React 组件中使用 useTranslation
function MyComponent() {
const { t, i18n } = useTranslation();
useEffect(() => {
const handleLanguageChange = () => {
// 语言变化时重新渲染
};
i18n.on('languageChanged', handleLanguageChange);
return () => {
i18n.off('languageChanged', handleLanguageChange);
};
}, [i18n]);
return <h1>{t('welcome')}</h1>;
}
// 确保翻译资源已加载
await i18next.loadLanguages(['zh', 'fr']);
```
### 3. 插值不工作
**问题**: 使用 `{{variable}}` 插值时变量没有被替换
**原因**:
- 插值配置错误
- 变量名拼写错误
- 转义设置问题
**解决方案**:
```javascript
// 检查插值配置
i18next.init({
interpolation: {
escapeValue: false, // React 中通常设为 false
prefix: '{{',
suffix: '}}'
}
});
// 确保变量名正确
t('greeting', { name: 'John' });
// 翻译资源
{
"greeting": "Hello {{name}}"
}
```
### 4. 复数处理不正确
**问题**: 复数形式显示不正确
**原因**:
- 复数规则配置错误
- 语言不支持默认复数规则
**解决方案**:
```javascript
// 为特定语言配置复数规则
i18next.init({
lng: 'ru',
resources: {
ru: {
translation: {
"item_one": "один предмет",
"item_few": "{{count}} предмета",
"item_many": "{{count}} предметов",
"item_other": "{{count}} предмет"
}
}
}
});
// 使用正确的复数键
t('item', { count: 1 }); // один предмет
t('item', { count: 2 }); // 2 предмета
t('item', { count: 5 }); // 5 предметов
```
### 5. 性能问题
**问题**: 翻译加载缓慢,影响应用性能
**原因**:
- 加载了过多翻译资源
- 没有使用缓存
- 网络请求过多
**解决方案**:
```javascript
// 使用延迟加载
i18next.init({
ns: ['translation'], // 只加载必要的命名空间
defaultNS: 'translation'
});
// 需要时加载其他命名空间
i18next.loadNamespaces(['admin', 'settings']);
// 使用缓存
import LocalStorageBackend from 'i18next-localstorage-backend';
i18next
.use(LocalStorageBackend)
.init({
backend: {
expirationTime: 7 * 24 * 60 * 60 * 1000 // 7天
}
});
// 预加载关键资源
i18next.init({
preload: ['en', 'zh']
});
```
### 6. 缺失翻译处理
**问题**: 缺失的翻译显示键名而不是回退文本
**解决方案**:
```javascript
// 配置缺失翻译处理
i18next.init({
fallbackLng: 'en',
saveMissing: true,
missingKeyHandler: (lng, ns, key) => {
console.warn(`Missing translation: ${lng}.${ns}.${key}`);
// 发送到翻译管理系统
reportMissingTranslation(lng, ns, key);
},
parseMissingKeyHandler: (key) => {
return `Translation missing: ${key}`;
}
});
```
### 7. SSR 问题
**问题**: 服务端渲染时翻译不显示
**解决方案**:
```javascript
// Next.js 示例
import { appWithTranslation } from 'next-i18next';
export default appWithTranslation(MyApp);
// 服务端获取翻译
export async function getServerSideProps({ locale }) {
return {
props: {
...(await serverSideTranslations(locale, ['common', 'home']))
}
};
}
```
### 8. 内存泄漏
**问题**: 组件卸载后事件监听器未清理
**解决方案**:
```javascript
function MyComponent() {
const { i18n } = useTranslation();
useEffect(() => {
const handleLanguageChange = () => {
console.log('Language changed');
};
i18n.on('languageChanged', handleLanguageChange);
// 清理事件监听器
return () => {
i18n.off('languageChanged', handleLanguageChange);
};
}, [i18n]);
return <div>Content</div>;
}
```
## 调试技巧
### 启用调试模式
```javascript
i18next.init({
debug: true, // 启用调试日志
initImmediate: false
});
```
### 检查翻译资源
```javascript
console.log('Current language:', i18next.language);
console.log('Loaded languages:', i18next.languages);
console.log('Translation data:', i18next.store.data);
console.log('Namespaces:', i18next.store.data[i18next.language]);
```
### 监听事件
```javascript
i18next.on('loaded', (loaded) => {
console.log('Resources loaded:', loaded);
});
i18next.on('failedLoading', (lng, ns, msg) => {
console.error('Failed to load:', { lng, ns, msg });
});
i18next.on('languageChanged', (lng) => {
console.log('Language changed to:', lng);
});
```
## 最佳实践
1. **错误处理**: 始终处理初始化和加载错误
2. **调试模式**: 开发环境启用调试模式
3. **日志记录**: 记录关键事件和错误
4. **性能监控**: 监控翻译加载性能
5. **单元测试**: 编写测试覆盖常见问题
6. **文档**: 记录配置和使用方法
7. **版本控制**: 使用版本号管理翻译资源
服务端 · 2月18日 22:06
如何优化 i18next 的性能?## 延迟加载
### 按需加载命名空间
```javascript
// 初始化时只加载必要的命名空间
i18next.init({
ns: ['translation', 'common'],
defaultNS: 'translation'
});
// 需要时加载其他命名空间
i18next.loadNamespaces(['admin', 'settings'], (err, t) => {
if (!err) {
console.log('命名空间加载完成');
}
});
```
### 按需加载语言
```javascript
// 初始化时只加载默认语言
i18next.init({
lng: 'en',
preload: ['en'] // 只预加载英语
});
// 用户切换语言时加载
i18next.loadLanguages(['zh', 'fr'], () => {
console.log('语言资源加载完成');
});
```
### React 中的 Suspense 模式
```javascript
import { useTranslation } from 'react-i18next';
import { Suspense } from 'react';
function LazyComponent() {
const { t } = useTranslation('lazyNamespace');
return <p>{t('content')}</p>;
}
function App() {
return (
<Suspense fallback={<div>Loading translations...</div>}>
<LazyComponent />
</Suspense>
);
}
```
## 缓存策略
### 本地存储缓存
```javascript
import LocalStorageBackend from 'i18next-localstorage-backend';
import HttpBackend from 'i18next-http-backend';
i18next
.use(HttpBackend)
.use(LocalStorageBackend)
.init({
backend: {
backends: [
LocalStorageBackend,
HttpBackend
],
backendOptions: [
{
expirationTime: 7 * 24 * 60 * 60 * 1000, // 7天过期
defaultVersion: 'v1.0.0',
store: window.localStorage
},
{
loadPath: '/locales/{{lng}}/{{ns}}.json'
}
]
}
});
```
### 内存缓存
```javascript
const translationCache = new Map();
function getCachedTranslation(key, options) {
const cacheKey = `${key}_${JSON.stringify(options)}`;
if (translationCache.has(cacheKey)) {
return translationCache.get(cacheKey);
}
const translation = i18next.t(key, options);
translationCache.set(cacheKey, translation);
return translation;
}
```
### 缓存失效策略
```javascript
backend: {
loadPath: '/locales/{{lng}}/{{ns}}.json?v={{version}}',
queryStringParams: {
version: process.env.TRANSLATION_VERSION || '1.0.0'
}
}
```
## 资源优化
### 压缩翻译资源
```javascript
// 使用 gzip 压缩
// 服务器配置示例 (Express)
const express = require('express');
const compression = require('compression');
const app = express();
app.use(compression());
app.use('/locales', express.static('locales'));
```
### 按功能拆分翻译文件
```javascript
// 文件结构
// locales/
// ├── en/
// │ ├── common.json
// │ ├── admin.json
// │ ├── user.json
// │ └── settings.json
// └── zh/
// ├── common.json
// ├── admin.json
// ├── user.json
// └── settings.json
// 配置
i18next.init({
ns: ['common', 'admin', 'user', 'settings'],
defaultNS: 'common'
});
```
### 移除未使用的翻译
```javascript
// 构建时分析代码中使用的翻译键
const usedKeys = analyzeTranslationKeysInCode();
const allTranslations = loadAllTranslations();
const optimizedTranslations = filterTranslations(allTranslations, usedKeys);
saveOptimizedTranslations(optimizedTranslations);
```
## 请求优化
### 批量加载
```javascript
// 一次性加载多个命名空间
Promise.all([
i18next.loadNamespaces(['admin', 'settings', 'reports']),
i18next.loadLanguages(['en', 'zh'])
]).then(() => {
console.log('所有翻译资源加载完成');
});
```
### 预加载关键资源
```javascript
i18next.init({
preload: ['en', 'zh'], // 预加载的语言
ns: ['translation', 'common'], // 预加载的命名空间
partialBundledLanguages: true // 允许部分加载
});
```
### 使用 CDN
```javascript
backend: {
loadPath: 'https://cdn.example.com/locales/{{lng}}/{{ns}}.json',
crossDomain: true
}
```
## 运行时优化
### 避免频繁翻译
```javascript
// 不好的做法
function renderList(items) {
return items.map(item => (
<div key={item.id}>
<h3>{t('item.title', { name: item.name })}</h3>
<p>{t('item.description', { desc: item.description })}</p>
</div>
));
}
// 好的做法 - 缓存翻译
function renderList(items) {
const titleTemplate = t('item.title');
const descTemplate = t('item.description');
return items.map(item => (
<div key={item.id}>
<h3>{titleTemplate.replace('{{name}}', item.name)}</h3>
<p>{descTemplate.replace('{{desc}}', item.description)}</p>
</div>
));
}
```
### 使用 React.memo 优化
```javascript
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
const TranslatedComponent = memo(({ text }) => {
const { t } = useTranslation();
return <span>{t(text)}</span>;
});
```
### 虚拟滚动优化长列表
```javascript
import { FixedSizeList } from 'react-window';
function TranslatedList({ items }) {
const { t } = useTranslation();
const Row = ({ index, style }) => (
<div style={style}>
{t(items[index].key)}
</div>
);
return (
<FixedSizeList
height={400}
itemCount={items.length}
itemSize={35}
>
{Row}
</FixedSizeList>
);
}
```
## 监控和分析
### 性能监控
```javascript
i18next.on('loaded', (loaded) => {
console.log('翻译资源加载完成:', loaded);
// 发送性能指标到监控系统
trackPerformance('i18n_loaded', {
namespaces: Object.keys(loaded),
timestamp: Date.now()
});
});
```
### 错误监控
```javascript
i18next.on('failedLoading', (lng, ns, msg) => {
console.error('翻译资源加载失败:', { lng, ns, msg });
// 发送错误到监控系统
trackError('i18n_load_failed', { lng, ns, msg });
});
```
## 最佳实践总结
1. **延迟加载**: 只加载当前需要的翻译资源
2. **缓存策略**: 使用本地存储和内存缓存减少网络请求
3. **资源优化**: 压缩、拆分和清理翻译文件
4. **请求优化**: 批量加载、预加载和使用 CDN
5. **运行时优化**: 避免频繁翻译、使用 memo 和虚拟滚动
6. **监控**: 监控加载性能和错误率
7. **版本控制**: 使用版本号管理翻译资源更新
服务端 · 2月18日 22:05
什么是 i18next 及其核心特性是什么?## 核心概念
i18next 是一个功能强大的国际化 JavaScript 库,专为现代 Web 应用程序设计。它提供了完整的翻译解决方案,支持多种框架和平台,包括 React、Vue、Angular 等。
### 主要特性
- **框架无关**: 可以在任何 JavaScript 环境中使用
- **命名空间支持**: 允许将翻译内容组织成不同的命名空间
- **插值功能**: 支持在翻译文本中插入动态变量
- **复数处理**: 内置对不同语言复数规则的支持
- **格式化**: 支持日期、数字和货币的本地化格式化
- **延迟加载**: 可以按需加载翻译资源,提高应用性能
- **缓存机制**: 自动缓存已加载的翻译资源
### 基本架构
i18next 采用模块化设计,核心库提供基础功能,通过插件扩展能力。主要模块包括:
- **i18next**: 核心库
- **i18next-browser-languagedetector**: 浏览器语言检测
- **i18next-http-backend**: HTTP 后端加载翻译资源
- **i18next-localstorage-backend**: 本地存储后端
### 使用场景
i18next 适用于需要多语言支持的 Web 应用程序,包括:
- 电商网站
- 企业管理系统
- 内容管理系统
- 社交媒体平台
- 在线教育平台
### 与其他库的对比
相比其他国际化库,i18next 的优势在于:
- 更完善的生态系统
- 更好的性能优化
- 更灵活的配置选项
- 更强大的插件系统
服务端 · 2月18日 22:05
如何管理 i18next 的翻译资源和工作流?## 翻译管理平台集成
### i18next-locize-backend
```javascript
import Locize from 'i18next-locize-backend';
i18next
.use(Locize)
.init({
backend: {
projectId: 'your-project-id',
apiKey: 'your-api-key',
referenceLng: 'en',
version: 'latest'
}
});
```
### Crowdin 集成
```javascript
// 使用 i18next-http-backend 从 Crowdin 加载
i18next
.use(HttpBackend)
.init({
backend: {
loadPath: 'https://cdn.crowdin.com/api/v2/projects/{{projectId}}/translations/{{lng}}/{{ns}}?apiKey={{apiKey}}',
queryStringParams: {
projectId: 'your-project-id',
apiKey: 'your-api-key'
}
}
});
```
## 自动翻译提取
### 使用 i18next-scanner
```bash
npm install --save-dev i18next-scanner
```
```javascript
// gulpfile.js
const gulp = require('gulp');
const scanner = require('i18next-scanner');
gulp.task('i18next', function() {
return gulp.src(['src/**/*.{js,jsx,ts,tsx}'])
.pipe(scanner({
lngs: ['en', 'zh', 'fr'],
resource: {
loadPath: 'locales/{{lng}}/{{ns}}.json',
savePath: 'locales/{{lng}}/{{ns}}.json'
},
keySeparator: '.',
nsSeparator: ':',
defaultValue: '__TRANSLATION__'
}))
.pipe(gulp.dest('locales'));
});
```
### 使用 Babel 插件
```javascript
// .babelrc
{
"plugins": [
["i18next-extract", {
"locales": ["en", "zh"],
"outputPath": "locales/{{locale}}/{{ns}}.json",
"keyAsDefaultValue": true
}]
]
}
```
## 翻译工作流
### 开发流程
1. **提取翻译键**: 使用扫描工具从代码中提取翻译键
2. **添加翻译**: 在翻译文件中添加或更新翻译
3. **提交代码**: 将翻译文件提交到版本控制
4. **发送翻译**: 将需要翻译的内容发送给翻译团队
5. **合并翻译**: 将翻译好的内容合并回代码库
### 自动化工作流
```javascript
// CI/CD 集成示例
const { execSync } = require('child_process');
// 提取翻译
execSync('npm run extract:translations');
// 检查是否有新增的翻译键
const newKeys = checkNewTranslationKeys();
if (newKeys.length > 0) {
console.log('发现新的翻译键:', newKeys);
// 发送通知或创建 issue
notifyTranslationTeam(newKeys);
}
// 运行测试
execSync('npm test');
```
## 翻译质量保证
### 翻译验证
```javascript
function validateTranslations(translations) {
const errors = [];
Object.keys(translations).forEach(lang => {
const langTranslations = translations[lang];
Object.keys(langTranslations).forEach(key => {
const translation = langTranslations[key];
// 检查是否包含插值占位符
if (translation.includes('{{') && !translation.includes('}}')) {
errors.push(`${lang}.${key}: 未闭合的插值占位符`);
}
// 检查是否包含 HTML 标签
if (/<[^>]*>/g.test(translation)) {
errors.push(`${lang}.${key}: 包含 HTML 标签`);
}
// 检查长度
if (translation.length > 500) {
errors.push(`${lang}.${key}: 翻译过长`);
}
});
});
return errors;
}
```
### 翻译覆盖率检查
```javascript
function checkTranslationCoverage(translations) {
const referenceLang = 'en';
const referenceKeys = Object.keys(translations[referenceLang]);
const coverage = {};
Object.keys(translations).forEach(lang => {
if (lang === referenceLang) return;
const langKeys = Object.keys(translations[lang]);
const missingKeys = referenceKeys.filter(key => !langKeys.includes(key));
coverage[lang] = {
total: referenceKeys.length,
translated: langKeys.length,
missing: missingKeys,
percentage: (langKeys.length / referenceKeys.length) * 100
};
});
return coverage;
}
```
## 翻译更新策略
### 增量更新
```javascript
async function updateTranslations(newTranslations) {
const currentTranslations = await loadCurrentTranslations();
Object.keys(newTranslations).forEach(lang => {
if (!currentTranslations[lang]) {
currentTranslations[lang] = {};
}
Object.assign(currentTranslations[lang], newTranslations[lang]);
});
await saveTranslations(currentTranslations);
}
```
### 版本控制
```javascript
// 使用 Git 追踪翻译变更
const { execSync } = require('child_process');
function commitTranslations(message) {
execSync('git add locales/');
execSync(`git commit -m "${message}"`);
execSync('git tag translations-v' + Date.now());
}
```
## 翻译平台集成
### Locize
```javascript
import locize from 'locize';
import locizeBackend from 'i18next-locize-backend';
const locizeOptions = {
projectId: 'your-project-id',
apiKey: 'your-api-key',
referenceLng: 'en',
version: 'latest'
};
i18next
.use(locizeBackend)
.use(locize.plugin())
.init({
backend: locizeOptions,
locizeLastUsed: locizeOptions,
saveMissing: true,
debug: true
});
```
### PhraseApp
```javascript
import PhraseBackend from 'i18next-phraseapp-backend';
i18next
.use(PhraseBackend)
.init({
backend: {
projectId: 'your-project-id',
apiKey: 'your-api-key',
version: 'latest'
}
});
```
## 最佳实践
1. **自动化提取**: 使用工具自动提取翻译键
2. **版本控制**: 将翻译文件纳入版本控制
3. **持续集成**: 在 CI/CD 中验证翻译质量
4. **翻译管理**: 使用专业的翻译管理平台
5. **质量保证**: 实施翻译验证和覆盖率检查
6. **团队协作**: 建立清晰的翻译工作流程
7. **文档维护**: 保持翻译文档的更新
服务端 · 2月18日 22:04
i18next-http-backend 如何实现翻译资源的远程加载?## i18next-http-backend 简介
i18next-http-backend 是 i18next 的一个插件,用于从远程服务器加载翻译资源。它支持 HTTP 请求、缓存和延迟加载等功能。
## 安装
```bash
npm install i18next-http-backend
# 或
yarn add i18next-http-backend
```
## 基本配置
### 简单配置
```javascript
import i18next from 'i18next';
import Backend from 'i18next-http-backend';
i18next
.use(Backend)
.init({
lng: 'en',
fallbackLng: 'en',
backend: {
loadPath: '/locales/{{lng}}/{{ns}}.json'
}
});
```
### 完整配置
```javascript
i18next
.use(Backend)
.init({
lng: 'en',
fallbackLng: 'en',
ns: ['translation', 'common', 'errors'],
defaultNS: 'translation',
backend: {
loadPath: '/locales/{{lng}}/{{ns}}.json',
addPath: '/locales/add/{{lng}}/{{ns}}',
parse: (data, lng, ns) => {
return JSON.parse(data);
},
stringify: (data, lng, ns) => {
return JSON.stringify(data);
},
request: (options, url, payload, callback) => {
// 自定义请求逻辑
fetch(url, options)
.then(response => response.json())
.then(data => callback(null, { data, status: 200 }))
.catch(error => callback(error, null));
},
reloadInterval: false, // 重新加载间隔(毫秒)
queryStringParams: { v: '1.0.0' } // 添加查询参数
}
});
```
## 路径配置
### 动态路径变量
- `{{lng}}`: 当前语言代码
- `{{ns}}`: 当前命名空间
- `{{projectId}}`: 自定义项目 ID
```javascript
backend: {
loadPath: '/api/translations/{{lng}}/{{ns}}?projectId={{projectId}}',
queryStringParams: {
projectId: 'my-project-123'
}
}
```
### 多路径配置
```javascript
backend: {
loadPath: (lngs, namespaces) => {
return namespaces.map(ns => `/locales/${lngs[0]}/${ns}.json`);
}
}
```
## 延迟加载
### 按需加载命名空间
```javascript
// 初始化时只加载默认命名空间
i18next
.use(Backend)
.init({
ns: ['translation'],
defaultNS: 'translation'
});
// 需要时加载其他命名空间
i18next.loadNamespaces(['admin', 'settings'], () => {
console.log('命名空间加载完成');
});
```
### React 中的延迟加载
```javascript
import { useTranslation } from 'react-i18next';
function AdminPanel() {
const { t, ready } = useTranslation('admin', { useSuspense: false });
if (!ready) {
return <div>Loading translations...</div>;
}
return <h1>{t('dashboard.title')}</h1>;
}
```
## 缓存机制
### 使用本地存储缓存
```javascript
import LocalStorageBackend from 'i18next-localstorage-backend';
import Backend from 'i18next-http-backend';
i18next
.use(Backend)
.use(LocalStorageBackend)
.init({
backend: {
backends: [
LocalStorageBackend, // 主后端
Backend // 回退后端
],
backendOptions: [
{
expirationTime: 7 * 24 * 60 * 60 * 1000, // 7天
defaultVersion: 'v1.0.0'
},
{
loadPath: '/locales/{{lng}}/{{ns}}.json'
}
]
}
});
```
### 自定义缓存逻辑
```javascript
backend: {
loadPath: '/locales/{{lng}}/{{ns}}.json',
request: (options, url, payload, callback) => {
const cacheKey = `i18n_${url}`;
const cached = localStorage.getItem(cacheKey);
if (cached) {
const { data, timestamp } = JSON.parse(cached);
const isExpired = Date.now() - timestamp > 3600000; // 1小时
if (!isExpired) {
return callback(null, { data, status: 200 });
}
}
fetch(url, options)
.then(response => response.json())
.then(data => {
localStorage.setItem(cacheKey, JSON.stringify({
data,
timestamp: Date.now()
}));
callback(null, { data, status: 200 });
})
.catch(callback);
}
}
```
## 错误处理
### 加载失败处理
```javascript
i18next
.use(Backend)
.init({
backend: {
loadPath: '/locales/{{lng}}/{{ns}}.json'
}
})
.catch(err => {
console.error('翻译资源加载失败:', err);
// 使用回退翻译
i18next.addResourceBundle('en', 'translation', {
welcome: 'Welcome',
error: 'An error occurred'
});
});
```
### 重试机制
```javascript
backend: {
loadPath: '/locales/{{lng}}/{{ns}}.json',
request: (options, url, payload, callback) => {
let retries = 3;
const attemptFetch = (attempt) => {
fetch(url, options)
.then(response => response.json())
.then(data => callback(null, { data, status: 200 }))
.catch(error => {
if (attempt < retries) {
setTimeout(() => attemptFetch(attempt + 1), 1000 * attempt);
} else {
callback(error, null);
}
});
};
attemptFetch(0);
}
}
```
## 性能优化
### 预加载关键翻译
```javascript
i18next
.use(Backend)
.init({
preload: ['en', 'zh'], // 预加载的语言
ns: ['translation', 'common'] // 预加载的命名空间
});
```
### 批量加载
```javascript
// 一次性加载多个命名空间
Promise.all([
i18next.loadNamespaces(['admin', 'settings', 'reports']),
i18next.loadLanguages(['en', 'zh', 'fr'])
]).then(() => {
console.log('所有翻译资源加载完成');
});
```
## 最佳实践
1. **版本控制**: 在 URL 中添加版本参数,避免缓存问题
2. **错误处理**: 实现完善的错误处理和回退机制
3. **性能优化**: 使用缓存和延迟加载减少网络请求
4. **监控**: 监控翻译资源加载性能和错误率
5. **CDN**: 使用 CDN 加速翻译资源加载
服务端 · 2月18日 18:14