React项目国际化-React-intl

  npx create-react-app react-intl-demo && cd react-intl-demo,创建react-intl-demo项目。npm install react-intl, 安装react-intl。使用react-intl时, 首先要提供一个provider,把整个项目包起来。怎么提供呢?使用react-intl的IntlProvider,它有1个必传参数locale, 设置本地语言,你的app要使用哪种语言进行渲染。你app的用户使用哪种语言,就要设置local为哪种语言, 比如en-us(美式英语)  zh-cn(简体中文)。
import {IntlProvider} from "react-intl";

ReactDOM.render(
  <React.StrictMode>
    <IntlProvider locale='en-us'>
      <App />
    </IntlProvider>
  </React.StrictMode>,
  document.getElementById('root')
);
  为什么要提供一个provider呢?就是为了让React组件能够使用国际化的函数,翻译文本。React Intl 使用Provider的模式为组件树提供i18n(国际化)的上下文。在根组件中提供配置本地语言和翻译信息,<Formatted*>组件可以直接使用。

  怎么翻译项目中的文本?翻译文件放到什么地方?通常来说,会为每一种语言提供一个翻译文件(json文件),src/translations/zh.json提供中文翻译 .  en.json file 提供英文翻译。假设要翻译App.js中的内容,src/translations/en.json

{
    "app.learn-react-link": "Learn React.",
    "app.text": "Edit <code>src/App.js</code> and save to reload."
}

  src/translations/zh.json

{
    "app.learn-react-link": "学习 React.",
    "app.text": "编辑<code>src/App.js</code>,保存,并刷新页面"
}

  项目中怎么获取到翻译文件? <IntlProvider />有messages属性, 把locale设置的语言对应的翻译内容,传给它,就可以为项目提供翻译内容。比如locale是en-us,那就要把en.json中的翻译内容传递给messsages. locale是zh-cn,那就把zh.json中的翻译内容传递给messages。怎么传递,只能先把json文件引入到项目中,再构建一个以locale为属性,翻译内容为值的对象,让messages的值根据locale来定。index.js中

import zh from './translations/zh.json';
import en from './translations/en.json';

const messages = {
  'en-us': en,
  'zh-cn': zh
}

const locale = 'zh-cn';

ReactDOM.render(
  <React.StrictMode>
    <IntlProvider locale={locale} messages={messages[locale]}>
      <App />
    </IntlProvider>
  </React.StrictMode>,
  document.getElementById('root')
);

  组件中怎么使用翻译内容?最简单的方法就是把要翻译的文本使用<FormattedMessage> 组件来代替。组件有一个必传属性id,用来引用翻译文件。怎么理解id呢?IntlProvider 的messages属性实际上是一个js对象,包含着项目中每一句文本的翻译,格式为{"app.learn-react-link": "学习 React."},"app.learn-react-link"可以看作组件中使用翻译时引用的id,react-intl自动会引用id 对应的翻译好的文本。<FormattedMessage> 中的id就是引用的这个id,也就是说,id必须和messages中指向json文件中的id一致。App.js

import {FormattedMessage} from 'react-intl';

<p><FormattedMessage id="app.text" /></p>
 
<a className="App-link" href="https://reactjs.org" target="_blank" rel="noopener noreferrer">
    <FormattedMessage id="app.learn-react-link" />
</a>

  npm start, 可以看到项目已经翻译成中文了。能不能把<code> 去掉呢?FormattedMessage 可以在value属性中解析出富文本中的XML标签。FormattedMessage 有一个value属性

<FormattedMessage id="app.text" 
  values={{
    code: chunks => <code>{chunks}</code>
  }}
>
</FormattedMessage>

  code对应的就是app.text中的<code></code>标签, 要解析富文本中的哪个标签,属性就是哪个标签。chunks就可解析出标签中的的内容,在这里就是code标签中的内容,src/App.js, 返回值就是替换整个标签及内容。但此时有个问题,如果有一种语言,恰好没有翻译呢?比如把locale改成en,

const locale = 'en';

  页面就只显示app.text和app.learn-react-link 等Id

 

   这时要用<FormattedMessage />的defaultMessage属性,设置默认展示的文本。 如果此文本没有在翻译文件中找到,就显示默认文本。它还有一个description: 描述这个文本的作用,主要为翻译者提供上下文。

<FormattedMessage id="app.text" 
  values={{
    code: chunks => <code>{chunks}</code>
  }}
  defaultMessage="编辑<code>src/App.js</code>,保存,并刷新页面"
  description="edit text"
>
<FormattedMessage id="app.learn-react-link" 
      defaultMessage="学习 React."
      description="text link"
/>
  但有时候,你不能使用组件,比如输入框的placeholder, 它只是纯文本,这就要用到formatMessage 函数。但使用formatMessage函数时,你需要一个intl的上下文。如果在函数组件中可以使用useIntl。写一个input输入框
import React from 'react';
import { useIntl } from 'react-intl';

export function Input() {
    const intl = useIntl();
    const placeholder = intl.formatMessage({id: 'app.placeholder'})
    return <input placeholder={placeholder}></input>
} 

  引入useIntl,在组件中调用它,返回intl上下文,调用formatMessage方法,它接受一个对象作为参数,id属性就是对应翻译文件中的id。不要忘了在,json文件中写

"app.placeholder": "请输入数字"

  当然,id属性可以是动态生成的,比如

const intlKey = "something"
const placeholder = intl.formatMessage({ id: `${intlKey}` })
  随着<FormattedMessage />用的越来越多,页面结构可能有点混乱,把id, defaultmessage, description 都写到组件中,太占空间。这时可以使用defineMessages 定义文本,就是用一个变量来指代这一系列的参数。单独创建一个文件 messages.js
import { defineMessages } from 'react-intl'
 
export default defineMessages({
    text: {
        id: 'app.text',
        defaultMessage: 'Edit <code>src/App.js</code>'
    },
    link: {
        id: 'app.learn-react-link',
        defaultMessage: 'Lerne React',
        description: 'Link on react page'
    },
     placeholder: {
        id: 'app.placeholder'
     }
});

  <FormattedMessage /> and formatMessage()就可以使用message.js中定义的text和link。App.js

import messages from './message.js'
import { FormattedMessage } from 'react-intl'

<p>
  <FormattedMessage {...messages.text}
    values={{
      code: chunks => <code>{chunks}</code>
    }}
  >
  </FormattedMessage>
</p>
 
<a
  className="App-link"
  href="https://reactjs.org"
  target="_blank"
>
  <FormattedMessage {...messages.link} />
</a>

  Input.js

import { useIntl } from 'react-intl';
import messages from './message'

export function Input() {
    const intl = useIntl();
    const placeholder = intl.formatMessage(messages.placeholder)
    return <input placeholder={placeholder}></input>
} 

  如果不能使用useIntl hooks, 在普通函数或类组件中可以使用injectIntl函数提供intl上下文。Input.js

import React from 'react';
import messages from './message.js'
import { injectIntl } from 'react-intl'

function Input({intl}) {
    const placeholder = intl.formatMessage(messages.placeholder)
    return <input placeholder={placeholder}></input>
}

export default injectIntl(Input);

  使用类

import React from 'react';
import messages from './message.js'
import { injectIntl } from 'react-intl'

class Input extends React.Component {
    render() {
        const {intl} = this.props;
        const placeholder = intl.formatMessage(messages.placeholder)
        return <input placeholder={placeholder}></input>
    }
}

export default injectIntl(Input);

 

posted @ 2022-03-12 15:02  SamWeb  阅读(2722)  评论(0编辑  收藏  举报