React组件的国际化与本地化
2021-08-016.1k 阅读
一、React 国际化与本地化简介
在当今全球化的互联网环境下,前端应用需要支持多种语言和地区设置,以满足不同用户群体的需求。React 作为主流的前端框架,提供了丰富的工具和方法来实现组件的国际化(Internationalization,简称 i18n)与本地化(Localization,简称 l10n)。
国际化主要涉及到将应用的文本内容、日期、时间、数字等可本地化的元素进行抽象和提取,使得应用能够根据用户的语言偏好动态加载相应的翻译文本。本地化则侧重于根据不同地区的文化、习惯等对应用进行定制,比如日期格式、货币显示、排序规则等。
二、React 国际化库介绍
- react - i18next
- 概述:react - i18next 是 React 应用中最常用的国际化库之一。它基于 i18next 库,提供了与 React 集成的高阶组件(HOC)和钩子(Hook),方便在 React 组件中进行国际化操作。
- 安装:可以通过 npm 或 yarn 安装。
npm install react - i18next i18next
# 或者
yarn add react - i18next i18next
- **基本使用**:
import React from'react';
import { useTranslation } from'react - i18next';
function MyComponent() {
const { t, i18n } = useTranslation();
return (
<div>
<p>{t('welcomeMessage')}</p>
<button onClick={() => i18n.changeLanguage('fr')}>Switch to French</button>
</div>
);
}
export default MyComponent;
在上述代码中,useTranslation
钩子用于获取翻译函数 t
和 i18next 实例 i18n
。t
函数用于翻译文本,i18n.changeLanguage
方法用于切换语言。
- react - intl
- 概述:react - intl 是由 FormatJS 团队开发的用于 React 应用国际化的库。它专注于格式化日期、时间、数字和货币等,同时也支持文本翻译。
- 安装:
npm install react - intl
# 或者
yarn add react - intl
- **基本使用**:
import React from'react';
import { IntlProvider, FormattedMessage } from'react - intl';
const messages = {
en: {
welcomeMessage: 'Welcome to our app'
},
fr: {
welcomeMessage: 'Bienvenue dans notre application'
}
};
function App() {
return (
<IntlProvider locale="en" messages={messages.en}>
<FormattedMessage id="welcomeMessage" />
</IntlProvider>
);
}
export default App;
这里,IntlProvider
组件用于提供国际化上下文,FormattedMessage
组件用于显示翻译后的文本。
三、配置 React 国际化
- 初始化 i18next
- 配置文件:在使用 react - i18next 时,通常需要创建一个 i18next 的配置文件。例如,在项目根目录下创建
i18n.js
文件。
- 配置文件:在使用 react - i18next 时,通常需要创建一个 i18next 的配置文件。例如,在项目根目录下创建
import i18next from 'i18next';
import { initReactI18next } from'react - i18next';
// 引入语言资源
import translationEN from './locales/en/translation.json';
import translationFR from './locales/fr/translation.json';
const resources = {
en: {
translation: translationEN
},
fr: {
translation: translationFR
}
};
i18next
.use(initReactI18next)
.init({
resources,
lng: 'en', // 默认语言
interpolation: {
escapeValue: false
}
});
export default i18next;
- **在应用中使用**:在 React 应用的入口文件(如 `index.js`)中导入配置好的 i18next。
import React from'react';
import ReactDOM from'react - dom';
import App from './App';
import i18next from './i18n';
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
- 配置 react - intl
- Provider 配置:在 react - intl 中,需要在应用的顶层组件包裹
IntlProvider
。
- Provider 配置:在 react - intl 中,需要在应用的顶层组件包裹
import React from'react';
import ReactDOM from'react - dom';
import App from './App';
import { IntlProvider } from'react - intl';
const messages = {
en: {
welcomeMessage: 'Welcome to our app'
},
fr: {
welcomeMessage: 'Bienvenue dans notre application'
}
};
ReactDOM.render(
<React.StrictMode>
<IntlProvider locale="en" messages={messages.en}>
<App />
</IntlProvider>
</React.StrictMode>,
document.getElementById('root')
);
- **动态切换语言**:要动态切换语言,需要重新渲染 `IntlProvider` 并传递新的 `locale` 和 `messages`。可以通过状态管理(如 Redux 或 React 上下文)来实现。
四、翻译文本
- 使用 react - i18next
- 简单文本翻译:如前面示例所示,使用
t
函数翻译文本。翻译键通常在语言资源文件(如translation.json
)中定义。
- 简单文本翻译:如前面示例所示,使用
import React from'react';
import { useTranslation } from'react - i18next';
function MyComponent() {
const { t } = useTranslation();
return <p>{t('greeting')}</p>;
}
export default MyComponent;
在 en/translation.json
中:
{
"greeting": "Hello"
}
在 fr/translation.json
中:
{
"greeting": "Bonjour"
}
- **带参数的翻译**:有时候翻译文本需要包含动态参数。
import React from'react';
import { useTranslation } from'react - i18next';
function MyComponent() {
const { t } = useTranslation();
const name = 'John';
return <p>{t('greetingWithName', { name })}</p>;
}
export default MyComponent;
在 en/translation.json
中:
{
"greetingWithName": "Hello, {{name}}!"
}
在 fr/translation.json
中:
{
"greetingWithName": "Bonjour, {{name}}!"
}
- 使用 react - intl
- 简单文本翻译:使用
FormattedMessage
组件。
- 简单文本翻译:使用
import React from'react';
import { IntlProvider, FormattedMessage } from'react - intl';
const messages = {
en: {
greeting: 'Hello'
},
fr: {
greeting: 'Bonjour'
}
};
function App() {
return (
<IntlProvider locale="en" messages={messages.en}>
<FormattedMessage id="greeting" />
</IntlProvider>
);
}
export default App;
- **带参数的翻译**:`FormattedMessage` 组件也支持带参数的翻译。
import React from'react';
import { IntlProvider, FormattedMessage } from'react - intl';
const messages = {
en: {
greetingWithName: 'Hello, {name}!'
},
fr: {
greetingWithName: 'Bonjour, {name}!'
}
};
function App() {
const name = 'John';
return (
<IntlProvider locale="en" messages={messages.en}>
<FormattedMessage id="greetingWithName" values={{ name }} />
</IntlProvider>
);
}
export default App;
五、日期和时间格式化
- 使用 react - intl
- 基本格式化:
react - intl
提供了FormattedDate
和FormattedTime
组件用于格式化日期和时间。
- 基本格式化:
import React from'react';
import { IntlProvider, FormattedDate, FormattedTime } from'react - intl';
const now = new Date();
function App() {
return (
<IntlProvider locale="en">
<div>
<FormattedDate value={now} />
<FormattedTime value={now} />
</div>
</IntlProvider>
);
}
export default App;
- **自定义格式化**:可以通过 `format` 属性自定义日期和时间的格式。
import React from'react';
import { IntlProvider, FormattedDate, FormattedTime } from'react - intl';
const now = new Date();
function App() {
return (
<IntlProvider locale="en">
<div>
<FormattedDate value={now} format="yyyy - MM - dd" />
<FormattedTime value={now} format="HH:mm:ss" />
</div>
</IntlProvider>
);
}
export default App;
- 使用 i18next - xhr - backend 和 moment - js(react - i18next 扩展)
- 安装依赖:
npm install i18next - xhr - backend moment moment - js
# 或者
yarn add i18next - xhr - backend moment moment - js
- **配置 i18next**:在 `i18n.js` 中添加如下配置。
import i18next from 'i18next';
import { initReactI18next } from'react - i18next';
import Backend from 'i18next - xhr - backend';
import moment from'moment';
import 'moment - js';
// 引入语言资源
import translationEN from './locales/en/translation.json';
import translationFR from './locales/fr/translation.json';
const resources = {
en: {
translation: translationEN
},
fr: {
translation: translationFR
}
};
i18next
.use(Backend)
.use(initReactI18next)
.init({
resources,
lng: 'en',
interpolation: {
escapeValue: false
},
backend: {
loadPath: '/locales/{{lng}}/{{ns}}.json'
}
});
// 配置 moment 语言
i18next.on('languageChanged', function (lng) {
moment.locale(lng);
});
export default i18next;
- **在组件中使用**:
import React from'react';
import { useTranslation } from'react - i18next';
import moment from'moment';
function MyComponent() {
const { t } = useTranslation();
const now = moment();
return <p>{t('currentDate', { date: now.format('YYYY - MM - DD') })}</p>;
}
export default MyComponent;
在 en/translation.json
中:
{
"currentDate": "The current date is {{date}}"
}
六、数字和货币格式化
- 使用 react - intl
- 数字格式化:
react - intl
提供了FormattedNumber
组件用于格式化数字。
- 数字格式化:
import React from'react';
import { IntlProvider, FormattedNumber } from'react - intl';
function App() {
const number = 1234.567;
return (
<IntlProvider locale="en">
<FormattedNumber value={number} />
</IntlProvider>
);
}
export default App;
- **货币格式化**:使用 `FormattedNumber` 组件并设置 `style` 为 `'currency'` 和 `currency` 属性。
import React from'react';
import { IntlProvider, FormattedNumber } from'react - intl';
function App() {
const amount = 1234.56;
return (
<IntlProvider locale="en" currency="USD">
<FormattedNumber value={amount} style="currency" currency="USD" />
</IntlProvider>
);
}
export default App;
- 使用 numeral - js(react - i18next 扩展)
- 安装依赖:
npm install numeral - js
# 或者
yarn add numeral - js
- **在组件中使用**:
import React from'react';
import { useTranslation } from'react - i18next';
import numeral from 'numeral - js';
function MyComponent() {
const { t } = useTranslation();
const number = 1234.567;
const formattedNumber = numeral(number).format('0,0.00');
return <p>{t('formattedNumber', { number: formattedNumber })}</p>;
}
export default MyComponent;
在 en/translation.json
中:
{
"formattedNumber": "The formatted number is {{number}}"
}
七、处理复数形式
- 使用 react - i18next
- 配置语言资源:在语言资源文件中定义复数形式。例如,在
en/translation.json
中:
- 配置语言资源:在语言资源文件中定义复数形式。例如,在
{
"itemCount": {
"one": "There is {{count}} item",
"other": "There are {{count}} items"
}
}
在 fr/translation.json
中:
{
"itemCount": {
"one": "Il y a {{count}} élément",
"other": "Il y a {{count}} éléments"
}
}
- **在组件中使用**:
import React from'react';
import { useTranslation } from'react - i18next';
function MyComponent() {
const { t } = useTranslation();
const count = 5;
return <p>{t('itemCount', { count, count: count })}</p>;
}
export default MyComponent;
- 使用 react - intl
- 配置语言资源:在
messages
对象中定义复数形式。
- 配置语言资源:在
const messages = {
en: {
itemCount: {
one: 'There is {count} item',
other: 'There are {count} items'
}
},
fr: {
itemCount: {
one: 'Il y a {count} élément',
other: 'Il y a {count} éléments'
}
}
};
- **在组件中使用**:
import React from'react';
import { IntlProvider, FormattedMessage } from'react - intl';
const messages = {
en: {
itemCount: {
one: 'There is {count} item',
other: 'There are {count} items'
}
},
fr: {
itemCount: {
one: 'Il y a {count} élément',
other: 'Il y a {count} éléments'
}
}
};
function App() {
const count = 3;
return (
<IntlProvider locale="en" messages={messages.en}>
<FormattedMessage id="itemCount" values={{ count }} />
</IntlProvider>
);
}
export default App;
八、本地化排序
- 字符串排序
- 使用
Intl.Collator
:在 JavaScript 中,可以使用Intl.Collator
进行本地化字符串排序。
- 使用
const fruits = ['banana', 'apple', 'cherry'];
const collator = new Intl.Collator('en', { sensitivity: 'base' });
fruits.sort(collator.compare);
console.log(fruits);
在 React 组件中,可以将其封装成一个函数并使用。
import React from'react';
function useLocalizedSort() {
const collator = new Intl.Collator('en', { sensitivity: 'base' });
return (array) => array.sort(collator.compare);
}
function MyComponent() {
const fruits = ['banana', 'apple', 'cherry'];
const sortedFruits = useLocalizedSort()(fruits);
return (
<ul>
{sortedFruits.map((fruit, index) => (
<li key={index}>{fruit}</li>
))}
</ul>
);
}
export default MyComponent;
- 数字排序
- 自定义排序函数:对于数字排序,虽然 JavaScript 的
Array.prototype.sort
本身可以处理基本的数字排序,但在本地化场景下,可能需要考虑不同地区的数字表示习惯。例如,一些地区使用逗号作为小数点,而另一些地区使用点号。
- 自定义排序函数:对于数字排序,虽然 JavaScript 的
function localizedNumberSort(a, b, locale) {
const numberA = Number(a.toString().replace(/[^\d.-]/g, ''));
const numberB = Number(b.toString().replace(/[^\d.-]/g, ''));
return numberA - numberB;
}
const numbers = ['1.23', '4,56', '7.89'];
const sortedNumbers = numbers.sort((a, b) => localizedNumberSort(a, b, 'en'));
console.log(sortedNumbers);
九、优化与性能考虑
- 代码拆分
- 动态导入语言资源:为了减少初始加载时间,可以使用动态导入语言资源。在 react - i18next 中,可以结合 Webpack 的动态导入功能。
import React from'react';
import { useTranslation } from'react - i18next';
function MyComponent() {
const { t, i18n } = useTranslation();
const loadTranslation = (lng) => {
import(`./locales/${lng}/translation.json`).then((translations) => {
i18n.addResourceBundle(lng, 'translation', translations.default);
i18n.changeLanguage(lng);
});
};
return (
<div>
<p>{t('message')}</p>
<button onClick={() => loadTranslation('fr')}>Switch to French</button>
</div>
);
}
export default MyComponent;
- 缓存翻译结果
- Memoization:在频繁翻译相同文本的场景下,可以使用 memoization 技术缓存翻译结果。例如,在 react - i18next 中,可以自定义一个 memoized 版本的
t
函数。
- Memoization:在频繁翻译相同文本的场景下,可以使用 memoization 技术缓存翻译结果。例如,在 react - i18next 中,可以自定义一个 memoized 版本的
import React from'react';
import { useTranslation } from'react - i18next';
function memoize(func) {
const cache = new Map();
return (key, options) => {
const cacheKey = `${key}:${JSON.stringify(options)}`;
if (cache.has(cacheKey)) {
return cache.get(cacheKey);
}
const result = func(key, options);
cache.set(cacheKey, result);
return result;
};
}
function MyComponent() {
const { t } = useTranslation();
const memoizedT = memoize(t);
return <p>{memoizedT('message')}</p>;
}
export default MyComponent;
十、测试国际化与本地化
- 单元测试
- 使用 Jest 和 react - testing - library:对于 React 组件的国际化测试,可以使用 Jest 和 react - testing - library。例如,测试一个使用 react - i18next 的组件。
import React from'react';
import { render, screen } from '@testing - library/react';
import { Provider } from'react - i18next';
import i18next from 'i18next';
import MyComponent from './MyComponent';
describe('MyComponent', () => {
beforeEach(() => {
i18next.init({
resources: {
en: {
translation: {
message: 'Test message'
}
}
},
lng: 'en',
interpolation: {
escapeValue: false
}
});
});
it('should render translated text', () => {
render(
<Provider i18n={i18next}>
<MyComponent />
</Provider>
);
expect(screen.getByText('Test message')).toBeInTheDocument();
});
});
- 集成测试
- 使用 Cypress:对于集成测试,可以使用 Cypress。例如,测试语言切换功能。
describe('Language Switching', () => {
it('should switch language', () => {
cy.visit('/');
cy.contains('Switch to French').click();
cy.contains('Bienvenue dans notre application').should('be.visible');
});
});
通过以上全面的介绍和代码示例,希望能帮助开发者在 React 项目中顺利实现组件的国际化与本地化,打造出更具全球化竞争力的前端应用。在实际项目中,需要根据项目的规模、需求和技术栈特点选择合适的国际化方案,并不断优化和测试,以提供良好的用户体验。