react 使用 react-intl包 配置国际化

630 阅读7分钟

1.antd组件国际化配置

因为我们的项目依赖于 antd 组件库,所以我们先进行antd的国际化配置 antd官网

import zhCN from 'antd/locale/zh_CN';
// for date-picker i18n
import 'dayjs/locale/zh-cn';

return (
  <ConfigProvider locale={zhCN}>
    <App />
  </ConfigProvider>
);

注意:这里的配置仅对 antd 组件预先设计好的文字生效,对我们自己定义的文字不起任何作用。所以之后我们会使用 react-int 对自己定义的文本进行国际化配置(antd的国际化配置和react-intl进行国际化配置无任何联系,请不要搞混淆了)

2.react-intl基本配置

1.首先下载 react-intl 包

npm install react-intl
#或者
yarn add react-intl

npm install intl //用于兼容性

在现代浏览器环境中,大多数已经内置了国际化 API,因此你可以在大多数情况下不需要下载 intl 包。然而,如果你需要支持一些旧的浏览器(比如 IE 11),则可能需要下载 npm install intl

注意: 要实现 intl 包对旧版本浏览器做兼容,只需要下载,然后在项目入口文件中引入就好,无需写任何代码和任何配置就可以实现低版本浏览器兼容

下面是一个详细的说明,说明如何在项目中引入 intl 以确保在低版本浏览器中的兼容性:

1. 安装 intl 包
npm install intl
import 'intl'; // 入口文件中导入 intl 用于兼容低版本浏览器
import 'locale-data/jsonp/en'; // 如果你的项目中使用了不同的语言,需要引入相应的语言包
import 'locale-data/jsonp/zh'; // 引入中文语言包

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

// 你项目的其他代码...

2.创建语言包en.json 和 zh.json文件(这里只对中英文切换做演示)


{
  "app.greeting": "Hello, World!",
  "hello":"hello"
}
{
  "app.greeting": "你好,世界!",
  "hello":"你好"
}

注意:json文件中的key必须是唯一的,否则会报错;再者两个文件中的key必须保持相同,这样才能实现文字的切换,最后,两个json文件中的数据条数必须保持一致,以避免程序出现错误

3.在App根组件中对react-intl进行配置

import React from 'react';
import ReactDOM from 'react-dom';
// import 'intl'; // 如果需要兼容低版本浏览器,可以在此导入 intl 用于兼容低版本浏览器
import { IntlProvider } from 'react-intl';
import App from './App';
import enMessages from './locales/en.json';
import zhMessages from './locales/zh.json';

const messages = {
  en: enMessages,
  zh: zhMessages
};

const language = navigator.language.split(/[-_]/)[0]; // 根据浏览器语言设置语言
// 为了减少代码的复杂度,这里没有将antd国际化一同在这配置,实际开发中记得另外配置antd的国际化
ReactDOM.render(
  <IntlProvider locale={language} messages={messages[language]}>
    <App />
  </IntlProvider>,
  document.getElementById('root')
);

这里对上面代码进行一个简单的解释:

1、import enMessages from './locales/en.json';import zhMessages from './locales/zh.json';这行代码导入的是本地的json文件,这里这个文件是需要自己去建立的,具体文件名称和位置可以随意配置,但是文件格式必须为json

2、navigator.language.split(/[-_]/)[0] ;这行代码获取的是浏览器的语言,实际开发中建议不要怎么操作,使用localStorge存储在本地会更加方便

3.react-intl的使用

当我们按照上方代码配置完毕后,我们就可以到组件中去使用他了,下面将讲解react-intl的几种使用方式

1.基本使用

//直接将此段代码替换你的文本即可,id则为自定义json文件中对应的
// defaultMessage 相当于一个默认值属性,当id对应的key缺失时,
//就会自动使用defaultMessage的值作为填充(这个属性可不配置)

<FormattedMessage id="app.greeting" defaultMessage="Hello, World!" />

//注意:这里的id和html中的id无任何联系,不会引起冲突,这里的id仅用于做国际化的标识
//编译后id将不会存在,无需有任何担心

案例

//未进行国际化配置
<Button type="primary">Primary Button</Button>
  <p>我是一名程序猿</p>

//配置国际化后
import React from 'react';
import { FormattedMessage } from 'react-intl';

const Greeting = () => (
  <div>
    <Button type="primary">
      <FormattedMessage id="此次替换为你在json文件中定义的key" defaultMessage="Hello, World!" />
    </Button>
    <p>
      {// defaultMessage 属性可以不配置}
        <FormattedMessage id="此次替换为你在json文件中定义的key" />
        </p>
        </div>
        );

      export default Greeting;

2.高级使用

1.消息模板语法
// json文件定义
{
  "welcomeMessage":"欢迎使用我们的应用!""count":"计数:{change}",
  "primaryButton":"主按钮",
  "defaultButton":"默认按钮",
  "dashedButton":"虚线按钮",
  "textButton":"文本按钮",
  "linkButton":"链接按钮"
}

//FormattedMessage可以配置values属性,用于动态加载json文件中定义{xxx}字符
//values的key必须和ison文件中的模板字符对应,“xxx”则为你在组件中动态拿到的值
<FormattedMessage id="count" values={{ change:"xxx" }} />

//因为values的格式为对象形式,所以可以配置多个动态值
{"day":"今天星期{change},有{change2}节数学课,还有{change3}"}节英语课"}
<FormattedMessage id="day" values={{ change:"4",change2:"2",change3:"3",}}/>
// 转换后的结果为:"今天星期4,有2节数学课,还有3节英语课”
2.给国际化文字写上特定的颜色
// json文件
{
  "welcomeMessage": "欢迎使用我们的应用!",
  "count": "计数: {change}",
  "primaryButton": "主按<span>钮</span>"
}

// 代码
import React from 'react';
import { FormattedMessage } from 'react-intl';

const App = () => {
  const change = <span style={{ color: 'red' }}>变化值</span>;

  return (
    <div>
      {/* 欢迎消息 */}
      <FormattedMessage id="welcomeMessage" />
      
      {/* 计数 */}
      <br />
      <FormattedMessage
        id="count"
        {/* 通过读取一个标签的方式来改变字体颜色而不是直接读取一个普通变量 */}
        values={{ change }} 
      />

      {/* 样式化按钮文字 */}
      <br />
      <FormattedMessage
        id="primaryButton"
        {/* 需要使用函数的方式去实现字体变色 */}
        values={{
          span: (chunks) => <span style={{ fontWeight: 'bold', color: 'blue' }}>{chunks}</span>
        }}
      />
    </div>
  );
};

export default App;
3.扩展——消息模板语法的更多使用:
{  
  "greeting": "Hello, {name}!",  
  "date": "Today is {ts, date, ::yyyyMMdd}",  
  "time": "The current time is {timeValue, time, short}",  
  "price": "The total price is {price, number, USD}",  
  "photos": "You have {photoCount, plural, =0{no photos} one{photo} other{# photos}}",  
  "userGender": "{gender, select, male{He is} female{She is} other{They are}}",  
  "completedTasks": "{taskCount, plural, =0{You have not completed any tasks} one{You have completed # task} other{You have completed # tasks}}",  
  "welcome": "{userName}, welcome to the platform. {link}",  
  "specialOffer": "Check out our special offer!"  
}
import React from 'react';
import { FormattedMessage } from 'react-intl';

const App = () => {
  const name = 'John';
  const ts = Date.now();
  const timeValue = new Date();
  const price = 12345.67;
  const photoCount = 5;
  const gender = 'male';
  const taskCount = 3;
  const userName = 'Alice';
  const link = <a href="https://www.example.com">点击这里</a>;

  return (
    <div>
      {/* 简单文本 */}
      {/* 转换后的内容:Hello, John! */}
      <FormattedMessage id="greeting" values={{ name }} />

      {/* 日期 */}
      {/* 转换后的内容:Today is 20230518 */}
      <FormattedMessage id="date" values={{ ts }} />

      {/* 时间 */}
      {/* 转换后的内容:The current time is 12:34 PM */}
      <FormattedMessage id="time" values={{ timeValue }} />

      {/* 数字 */}
      {/* 转换后的内容:The total price is $12,345.67 */}
      <FormattedMessage id="price" values={{ price }} />

      {/* 复数 */}
      {/* 转换后的内容:You have 5 photos */}
      <FormattedMessage id="photos" values={{ photoCount }} />

      {/* 选择 */}
      {/* 转换后的内容:He is */}
      <FormattedMessage id="userGender" values={{ gender }} />

      {/* 更复杂的复数结构 */}
      {/* 转换后的内容:You have completed 3 tasks */}
      <FormattedMessage id="completedTasks" values={{ taskCount }} />

      {/* 包含链接的欢迎消息 */}
      {/* 转换后的内容:Alice, welcome to the platform. 点击这里 */}
      <FormattedMessage id="welcome" values={{ userName, link: link }} />

      {/* 特别优惠 */}
      {/* 转换后的内容:Check out our special offer! */}
      <FormattedMessage id="specialOffer" />
    </div>
  );
};

export default App;

案例:

// json文件定义
{
  "count":“计数:{count}",
    "day":"今天星期{change},有{change2}节数学课,还有{change3}"}节英语课"
}

import React from 'react';
import { FormattedMessage } from 'react-intl';

const Greeting = () => (
  const [count,setCount]=useState(8);
<>
  {/* demo1 */}
  <p><FormattedMessage id="count"values={{ count }}/>
    {/* 页面渲染后的内容为<p>计数:8</p> */}
  </p>

  {/* demo2 */}
  <span>
    <FormattedMessage id="day" values={{ change:"4",change2:"2",change3:"3"}}
      </span>
  {/* 页面渲染后的内容为<span>今天星期4,有2节数学课,还有3节英语课</span> */}</>
);

export default Greeting;

3.编程式使用

// 当你需要在组件中插入翻译文本,但又不在 JSX 返回的模板中时
//(比如设置文档标题、生成表单选项等非直接渲染的场合),intl.formatMessage()就特别有用

//可以直接将此段代码替换你的文本
{intl.formatMessage({ id:"clickMe"})}

//intl.formatMessage({id:'pageTitle'});也能使用消息模板,和FormattedMessage类似
// json文件定义
{
  "greeting":"Hello,{name}!Welcome back to {location}."
}

// 使用
intl.formatMessage({
  id:'greeting',
  values:{
    name: 'Alice',
    location: "Wonderland'
  }
});

//转换后:Hello, Alice! Welcome back to Wonderland.
//注意:这里的1d和html中的id无任何联系,不会引起冲突,这里的id仅用于做国际化的标识
//编译后id将不会存在,无需有任何担心

案例

import { useIntl } from 'react-intl';
function MyComponent(){
  const intl= useIntl();
  //使用 intl 创建动态文档标题
  useEffect(()=>{
    document.title=intl.formatMessage({ id:'pageTitle'});
  },[intl]);
}

说明:通过上面的例子,大家不难发现 int.formatMessage(id:"clickMe”))不仅可以在编程式中使用,使用差值语法的方式在isx中使用也是OK的。虽然不会有什么问题,但是这里不建议这么做,这样违背了react-intl 创建者的意图。在组件中还是建议使用<FormattedMessage id="count" values={{count }}/>这种形式,在编程逻辑中可以使用 intl.formatMessage({id:"clickMe"})这种形式

4. 切换语言

import React, { useState } from 'react';
import { IntlProvider } from 'react-intl';
import enMessages from './locales/en.json';
import zhMessages from './locales/zh.json';
import Greeting from './Greeting';

const messages = {
  en: enMessages,
  zh: zhMessages
};

const App = () => {
  const [locale, setLocale] = useState('en');

  const switchLanguage = (language) => {
    setLocale(language);
  };

  return (
    <IntlProvider locale={locale} messages={messages[locale]}>
      <div>
        <button onClick={() => switchLanguage('en')}>English</button>
        <button onClick={() => switchLanguage('zh')}>中文</button>
        <Greeting />
      </div>
    </IntlProvider>
  );
};

export default App;

5.如何解决文本动态加载翻译问题

注意:这一情况是针对于国际化业务的,提供解决问题思路,与任何npm无关,可以被任何国际化场景借鉴或使用有一种情况,当消息模板中的文字是动态加载的,我该如何翻译呢?例如值可能是”loading”,"success",或"error",那我们怎么翻译不同的值?

//如果你有一个状态消息,可能是"loading""success",或“error"
// 那么你可以在你的国际化消息中为每个状态定义一个条目:

// json文件定义
{
  "status_loading":"正在加载...",
  "status_success":"加载成功!",
  "status_error":"加载失败。"
}

// 使用
<FormattedMessage id={`status ${status}`} />

// 如果返回的是"加载中,成功,失败”这些中文也同样可以使用
{
  "status_加载中":"正在加载...",
  "status_成功":"加载成功!",
  "status_失败":"加载失败。"
}
//使用
<FormattedMessage id={`status_${status}`} />

最后,还有一个最为极端的情况,就是如果返回的是随机一段中文文本,那该如何解决?那么将很难实现,因为标准的国际化实践是基于静态键值对的,依赖预先定义好的翻译映射。但对于随机或动态生成的文本(完全随机的情况),这里可以提供几种实现思路:

1.调用第三方翻译的api实现文本翻译

2.后端翻译,后端将同时提供几种语言的字段,前端用来动态取值

注意:本篇文章中,只针对了中英文两种语言的切换,但实际上是可以对于多种语言的切换,并不局限于某几种语言,多种语言需要定义多个json文件,这个在你熟读完整篇文章后可以去尝试多语言的切换。 注意:这里无论是FormattedMessage中的id还是formatMessage中的id,都与html中的id无任何联系,不会引起冲突,这里的id仅用于做国际化的标识, 编译后id将不会存在,无需有任何担心

插入:小编自己封装了一个大文件上传的工具函数,支持切片上传、错误重试、上传进度、暂停和继续等,目前已发布在npm官网,感兴趣的可以看看。

www.npmjs.com/package/enl…

下载命令:npm i enlarge-file-upload