前端国际化实现方案
常见误区
国际化并非简单的文本替换。不同语言的语法结构差异可能导致直接替换出现错误。常见的误区包括:
- 硬编码文本:难以维护和翻译。
- 简单文本替换:无法处理复杂的翻译场景。
- 忽略复数形式:在不同语言中可能不正确。
- 忽略日期和时间格式:在不同地区可能显示错误。
- 忽略货币格式:在不同国家可能显示错误。
反面示例代码:
// 硬编码文本
function Welcome() {
return <h1>Welcome to our app!</h1>;
}
// 简单文本替换
const translations = {
en: { welcome: 'Welcome to our app!', login: 'Login', register: 'Register' },
zh: { welcome: '欢迎使用我们的应用!', login: '登录', register: '注册' }
};
function Welcome() {
const lang = 'zh';
return <h1>{translations[lang].welcome}</h1>;
}
正确的做法
使用 i18next
-
安装依赖
npm install i18next react-i18next -
配置
import i18n from 'i18next'; import { initReactI18next } from 'react-i18next'; const resources = { en: { translation: { welcome: 'Welcome to our app!', login: 'Login', register: 'Register' } }, zh: { translation: { welcome: '欢迎使用我们的应用!', login: '登录', register: '注册' } } }; i18n .use(initReactI18next) .init({ resources, lng: 'en', fallbackLng: 'en', interpolation: { escapeValue: false } }); export default i18n; -
使用
import React from 'react'; import { useTranslation } from 'react-i18next'; function Welcome() { const { t } = useTranslation(); return <h1>{t('welcome')}</h1>; } -
切换语言
import React from 'react'; import { useTranslation } from 'react-i18next'; function LanguageSelector() { const { i18n } = useTranslation(); const changeLanguage = (lng) => { i18n.changeLanguage(lng); }; return ( <div> <button onClick={() => changeLanguage('en')}>English</button> <button onClick={() => changeLanguage('zh')}>中文</button> </div> ); }
处理复数形式
// 配置复数规则
const resources = {
en: {
translation: {
itemCount: 'You have {{count}} item in your cart.',
itemCount_plural: 'You have {{count}} items in your cart.'
}
},
zh: {
translation: {
itemCount: '您的购物车中有 {{count}} 件商品。'
}
}
};
// 使用复数形式
function ItemCount({ count }) {
const { t } = useTranslation();
return <p>{t('itemCount', { count, plural: count !== 1 })}</p>;
}
处理日期和时间格式
// 使用 Intl.DateTimeFormat
function FormatDate({ date }) {
const { i18n } = useTranslation();
const locale = i18n.language;
return (
<p>
{new Intl.DateTimeFormat(locale, {
year: 'numeric',
month: 'long',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
}).format(date)}
</p>
);
}
处理货币格式
// 使用 Intl.NumberFormat
function FormatPrice({ price, currency = 'USD' }) {
const { i18n } = useTranslation();
const locale = i18n.language;
return (
<p>
{new Intl.NumberFormat(locale, { style: 'currency', currency }).format(price)}
</p>
);
}
处理 RTL 语言
// 检测 RTL 语言
function isRTL(lang) {
const rtlLanguages = ['ar', 'he', 'fa', 'ur'];
return rtlLanguages.includes(lang);
}
// 设置 RTL 样式
function App() {
const { i18n } = useTranslation();
useEffect(() => {
if (isRTL(i18n.language)) {
document.documentElement.dir = 'rtl';
document.documentElement.lang = i18n.language;
} else {
document.documentElement.dir = 'ltr';
document.documentElement.lang = i18n.language;
}
}, [i18n.language]);
return <div>应用内容</div>;
}
最佳实践
-
分离翻译文件
// public/locales/en/translation.json { "welcome": "Welcome to our app!", "login": "Login" } -
配置后端加载
import Backend from 'i18next-http-backend'; import LanguageDetector from 'i18next-browser-languagedetector'; i18n .use(Backend) .use(LanguageDetector) .use(initReactI18next) .init({ fallbackLng: 'en', interpolation: { escapeValue: false } }); -
使用命名空间
function Home() { const { t } = useTranslation(['home', 'common']); return ( <div> <h1>{t('welcome')}</h1> <button>{t('login', { ns: 'common' })}</button> </div> ); } -
动态加载翻译
useEffect(() => { i18n.loadNamespaces('dynamic'); }, [i18n]);
总结与建议
国际化确实很重要,但需避免过度设计。为了支持多语言创建大量翻译文件会增加维护成本。应根据实际情况决定国际化的范围。对于面向全球用户的应用,国际化是必要的;但对于只面向特定地区用户的应用,过度的国际化反而会增加开发成本和维护难度。记住,国际化的目的是为了提高用户体验,而不是炫技。

