在不使用外部库的情况下在 React 中切换语言
Switch the language in React without using external libraries
在不使用外部库的情况下,您可以推荐哪些在 React 中更改语言的方法?我的方法是使用三元运算符{language === 'en'? 'title': 'titre'}
。如果语言是en
,则显示title
,否则显示titre
。你还能推荐什么其他方式。例如,翻译应放在单独的 json 文件中。
代码在这里:https://stackblitz.com/edit/react-eu9myn
class App extends Component {
constructor() {
super();
this.state = {
language: 'en'
};
}
changeLanguage = () => {
this.setState({
language: 'fr'
})
}
render() {
const {language} = this.state;
return (
<div>
<p>
{language === 'en' ? 'title' : 'titre'}
</p>
<button onClick={this.changeLanguage}>change language</button>
</div>
);
}
}
国际化 (i18n) 是一个难题,现有的一些标准解决方案由翻译专家和语言学家设计,以解决世界各地语言怪癖的广度。您通常不应该尝试提出自己的解决方案,即使您精通所有目标语言也是如此。
这并不意味着您需要一个库(您可以自己实现其中一个标准)但是编写内联的 i18n 逻辑将无法扩展并且可能不会很好地工作。
最简单的 i18n 情况是,如果您翻译的字符串不依赖于上下文并且是没有插值的完整句子。你可以在那里使用一个非常基本的方法,比如使用一个大的翻译词典并只查找其中的每个字符串。它看起来有点像你的三元,但至少它可以扩展到多种语言,并且在没有库的情况下这样做是合理的:
l10n = {
'title': {en: 'title', fr: 'titre'}
}
<p>
{l10n['title'][lang]}
</p>
但是,如果要在您的 website/application/whatever 中进行字符串插值,请考虑一个实现 ICU.
的库
现在,让我告诉你为什么这是个坏主意。假设你有一个字符串 "you can see (n) rocks",你想用一个实际数字替换 (n),并且你希望这个句子在语法上是正确的,所以你需要计算数字一致性,对吗?所以,“0 rocks”、“1 rocks”、“2+ rocks”……看起来英语复数只是添加一个 "s" (不正确,但我们现在假设),你可以用三元来实现它。我看到你在你的例子中使用了法语,那怎么样? “0 个卡由”、“1 个卡由”、“2+ 个卡由”。是的,法语有多种复数形式。您如何编写代码来说明这一点?如果您需要德语翻译怎么办?也许译者会决定宾语应该放在句子的最前面,而不是最后。您的代码如何处理基于语言的词序?
所有这些问题都应该委托给译者,译者将它们编码成 ICU 字符串,然后在给定上下文的情况下由一些代码对其进行评估以获得正确的翻译。无论你是使用一个库还是自己实现它,你最终想要的是一些函数——我们称它为 localize(string, context)
,它几乎独立于 React 并且你在你的组件中使用这样的函数:
import localize from './somewhere'
<p>
{localize('title')}
</p>
如果你真的想要,你可以将语言环境作为参数传递,并以某种方式将其存储在 React 的状态中。 This library 认为没有必要,因为真正的用户很少切换语言,并且在发生这种情况时重新加载整个应用程序是可以的。
我刚刚为使用本地化 context/provider 和字典(例如 JSON)的工作实现了一个简单的语言组件。我将完成这些步骤,最后有一个可行的 codesandbox 示例。这是一个非常基本的方法,但目前对我们来说效果很好。
示例有:
1) 一个简单的 "dictionary",其中包含您要翻译的每种语言的标记,由短代码定义
{ EN: { welcome: 'Welcome' }, FR: { welcome: 'Bienvenue' }, IT: { welcome: 'Benvenuto' } };
2) 语言变化时可以更新的初始状态和 reducer
export const initialState = {
defaultLanguage: 'EN',
selectedLanguage: 'IT'
}
export function reducer(state, action) {
const { type, payload } = action;
switch (type) {
case 'LANGUAGE_UPDATE': {
return { ...state, selectedLanguage: payload };
}
default: return state;
}
}
3) 本地化 Context/Provider。您可以将代码包装在提供者中,每个子组件都可以通过上下文访问状态。我们导入字典和 state/reducer,创建新的上下文,然后设置我们将状态和字典传递到其中的提供者。
import dictionary from './dictionary';
import { initialState, reducer } from './localisationReducer';
export const LocalisationContext = React.createContext();
export function LocalisationProvider({ children }) {
const localisationStore = useReducer(reducer, initialState);
return (
<LocalisationContext.Provider value={{ localisationStore, dictionary }}>
{children}
</LocalisationContext.Provider>
);
}
4) 一个示例应用程序。您可以看到 LocalisationProvider 包装了其他元素,还有一个下拉列表和一个名为 Translate 的组件。接下来我会描述这些。
<LocalisationProvider>
<Dropdown />
<div className="App">
<h1>
<Translate token="welcome" />
</h1>
<h2>Start editing to see some magic happen!</h2>
</div>
</LocalisationProvider>
5) 下拉菜单访问本地化上下文并构建包含语言的下拉菜单。关键部分是 handleSelected
函数,它使用来自本地化存储的 dispatch
来更改状态(更新语言):
import { LocalisationContext } from './localisation';
const langs = [
{ shortCode: 'EN', label: 'English' },
{ shortCode: 'FR', label: 'Français' },
{ shortCode: 'IT', label: 'Italiano' }
];
export function Dropdown() {
const {
localisationStore: [ state, dispatch ]
} = useContext(LocalisationContext);
const { selectedLanguage } = state;
const handleSelected = (e) => {
const { target: { value } } = e;
dispatch({ type: 'LANGUAGE_UPDATE', payload: value });
}
function getOptions(langs, selectedLanguage) {
return langs.map(({ shortCode, label }) => {
return <option value={shortCode}>{label}</option>
});
}
return (
<select onChange={handleSelected}>
{getOptions(langs, selectedLanguage)}
</select>
);
}
6) Translate 组件也通过上下文访问状态和字典,并根据 selected 语言进行翻译。
import { LocalisationContext } from './localisation';
export default function Translator({ token }) {
const {
localisationStore: [state], dictionary
} = useContext(LocalisationContext);
const {
selectedLanguage, defaultLanguage
} = state;
const translatedToken = dictionary[selectedLanguage][token] || dictionary[defaultLanguage][token];
return (
<Fragment>
{translatedToken}
</Fragment>
);
}
这是codesandbox example for you to explore。只需 select 下拉列表中的一种新语言即可查看主要 "welcome" 文本更改。
在不使用外部库的情况下,您可以推荐哪些在 React 中更改语言的方法?我的方法是使用三元运算符{language === 'en'? 'title': 'titre'}
。如果语言是en
,则显示title
,否则显示titre
。你还能推荐什么其他方式。例如,翻译应放在单独的 json 文件中。
代码在这里:https://stackblitz.com/edit/react-eu9myn
class App extends Component {
constructor() {
super();
this.state = {
language: 'en'
};
}
changeLanguage = () => {
this.setState({
language: 'fr'
})
}
render() {
const {language} = this.state;
return (
<div>
<p>
{language === 'en' ? 'title' : 'titre'}
</p>
<button onClick={this.changeLanguage}>change language</button>
</div>
);
}
}
国际化 (i18n) 是一个难题,现有的一些标准解决方案由翻译专家和语言学家设计,以解决世界各地语言怪癖的广度。您通常不应该尝试提出自己的解决方案,即使您精通所有目标语言也是如此。
这并不意味着您需要一个库(您可以自己实现其中一个标准)但是编写内联的 i18n 逻辑将无法扩展并且可能不会很好地工作。
最简单的 i18n 情况是,如果您翻译的字符串不依赖于上下文并且是没有插值的完整句子。你可以在那里使用一个非常基本的方法,比如使用一个大的翻译词典并只查找其中的每个字符串。它看起来有点像你的三元,但至少它可以扩展到多种语言,并且在没有库的情况下这样做是合理的:
l10n = {
'title': {en: 'title', fr: 'titre'}
}
<p>
{l10n['title'][lang]}
</p>
但是,如果要在您的 website/application/whatever 中进行字符串插值,请考虑一个实现 ICU.
的库现在,让我告诉你为什么这是个坏主意。假设你有一个字符串 "you can see (n) rocks",你想用一个实际数字替换 (n),并且你希望这个句子在语法上是正确的,所以你需要计算数字一致性,对吗?所以,“0 rocks”、“1 rocks”、“2+ rocks”……看起来英语复数只是添加一个 "s" (不正确,但我们现在假设),你可以用三元来实现它。我看到你在你的例子中使用了法语,那怎么样? “0 个卡由”、“1 个卡由”、“2+ 个卡由”。是的,法语有多种复数形式。您如何编写代码来说明这一点?如果您需要德语翻译怎么办?也许译者会决定宾语应该放在句子的最前面,而不是最后。您的代码如何处理基于语言的词序?
所有这些问题都应该委托给译者,译者将它们编码成 ICU 字符串,然后在给定上下文的情况下由一些代码对其进行评估以获得正确的翻译。无论你是使用一个库还是自己实现它,你最终想要的是一些函数——我们称它为 localize(string, context)
,它几乎独立于 React 并且你在你的组件中使用这样的函数:
import localize from './somewhere'
<p>
{localize('title')}
</p>
如果你真的想要,你可以将语言环境作为参数传递,并以某种方式将其存储在 React 的状态中。 This library 认为没有必要,因为真正的用户很少切换语言,并且在发生这种情况时重新加载整个应用程序是可以的。
我刚刚为使用本地化 context/provider 和字典(例如 JSON)的工作实现了一个简单的语言组件。我将完成这些步骤,最后有一个可行的 codesandbox 示例。这是一个非常基本的方法,但目前对我们来说效果很好。
示例有:
1) 一个简单的 "dictionary",其中包含您要翻译的每种语言的标记,由短代码定义
{ EN: { welcome: 'Welcome' }, FR: { welcome: 'Bienvenue' }, IT: { welcome: 'Benvenuto' } };
2) 语言变化时可以更新的初始状态和 reducer
export const initialState = {
defaultLanguage: 'EN',
selectedLanguage: 'IT'
}
export function reducer(state, action) {
const { type, payload } = action;
switch (type) {
case 'LANGUAGE_UPDATE': {
return { ...state, selectedLanguage: payload };
}
default: return state;
}
}
3) 本地化 Context/Provider。您可以将代码包装在提供者中,每个子组件都可以通过上下文访问状态。我们导入字典和 state/reducer,创建新的上下文,然后设置我们将状态和字典传递到其中的提供者。
import dictionary from './dictionary';
import { initialState, reducer } from './localisationReducer';
export const LocalisationContext = React.createContext();
export function LocalisationProvider({ children }) {
const localisationStore = useReducer(reducer, initialState);
return (
<LocalisationContext.Provider value={{ localisationStore, dictionary }}>
{children}
</LocalisationContext.Provider>
);
}
4) 一个示例应用程序。您可以看到 LocalisationProvider 包装了其他元素,还有一个下拉列表和一个名为 Translate 的组件。接下来我会描述这些。
<LocalisationProvider>
<Dropdown />
<div className="App">
<h1>
<Translate token="welcome" />
</h1>
<h2>Start editing to see some magic happen!</h2>
</div>
</LocalisationProvider>
5) 下拉菜单访问本地化上下文并构建包含语言的下拉菜单。关键部分是 handleSelected
函数,它使用来自本地化存储的 dispatch
来更改状态(更新语言):
import { LocalisationContext } from './localisation';
const langs = [
{ shortCode: 'EN', label: 'English' },
{ shortCode: 'FR', label: 'Français' },
{ shortCode: 'IT', label: 'Italiano' }
];
export function Dropdown() {
const {
localisationStore: [ state, dispatch ]
} = useContext(LocalisationContext);
const { selectedLanguage } = state;
const handleSelected = (e) => {
const { target: { value } } = e;
dispatch({ type: 'LANGUAGE_UPDATE', payload: value });
}
function getOptions(langs, selectedLanguage) {
return langs.map(({ shortCode, label }) => {
return <option value={shortCode}>{label}</option>
});
}
return (
<select onChange={handleSelected}>
{getOptions(langs, selectedLanguage)}
</select>
);
}
6) Translate 组件也通过上下文访问状态和字典,并根据 selected 语言进行翻译。
import { LocalisationContext } from './localisation';
export default function Translator({ token }) {
const {
localisationStore: [state], dictionary
} = useContext(LocalisationContext);
const {
selectedLanguage, defaultLanguage
} = state;
const translatedToken = dictionary[selectedLanguage][token] || dictionary[defaultLanguage][token];
return (
<Fragment>
{translatedToken}
</Fragment>
);
}
这是codesandbox example for you to explore。只需 select 下拉列表中的一种新语言即可查看主要 "welcome" 文本更改。