非组件的 React-intl

Posted

技术标签:

【中文标题】非组件的 React-intl【英文标题】:React-intl for non components 【发布时间】:2018-05-26 21:18:24 【问题描述】:

目前我有以下代码将 react-intl 暴露给非组件,但它会抛出一个错误,因为 intl as undefined。

我已经创建了一个单独的组件作为“CurrentLocale”并向它注入 intl。导出函数 t 将使用来自 CurrentLocale 上下文的 intl formatMessage。

import React from 'react';
import injectIntl from 'react-intl';
import PropTypes from 'prop-types';
import  flow  from 'lodash';

class CurrentLocale extends React.Component 


    constructor(props,context)

        super();
        console.log(context,props);
        console.log(this.formatMessage);
        const  intl  = this.context.intl;//this.props;

        this.formatMessage = intl.formatMessage;
    

    render() 
        return false;
    

CurrentLocale.contextTypes=
    intl:PropTypes.object,
;


injectIntl(CurrentLocale);

function intl() 
    return new CurrentLocale();


function formatMessage(...args) 
    return intl().formatMessage(...args);



const t = opts => 
    const id = opts.id;
    const type = opts.type;
    const values = opts.values;
    let t;

    switch (type)

        case 'message':
        default:
            t = formatMessage(id, values);
    

    return t;


export default t;

t 在另一个纯 javascript 文件中被称为,

import t from './locale/t';
t(  type: 'message', id:'button.Next');

以下是错误消息。 提前致谢。

【问题讨论】:

【参考方案1】:

我还使用另一种非常简单的方法来解决类似问题:为非组件提供对 intl 对象的访问:

import  IntlProvider, addLocaleData  from 'react-intl';
import localeDataDE from 'react-intl/locale-data/de';
import localeDataEN from 'react-intl/locale-data/en';
import  formMessages  from '../../../store/i18n'; // I defined some messages here
import  Locale  from '../../../../utils'; //I set the locale fom here

addLocaleData([...localeDataEN, ...localeDataDE]);
const locale = Locale.setLocale(); //my own methods to retrieve locale
const messages = Locale.setMessages(); //getting messages from the json file.
const intlProvider = new IntlProvider( locale, messages );
const  intl  = intlProvider.getChildContext();

export const SCHEMA = 
  salutation: 
    label: intl.formatMessage(formMessages.salutationLabel),
    errormessages: 
      required: intl.formatMessage(formMessages.salutationError),
    ,
  ,
  academic_title_code: 
    label: intl.formatMessage(formMessages.academicTitleLabel),
  ,
;

它就像一个魅力!

v3.x 更新

After migration to react-intl 3.x

import  createIntl, createIntlCache  from 'react-intl'
import  formMessages  from '../../../store/i18n'; // I defined some messages here
import  Locale  from '../../../../utils'; //I set the locale fom here

const locale = Locale.setLocale(); //my own methods to retrieve locale
const messages = Locale.setMessages(); //getting messages from the json file.
// This is optional but highly recommended
// since it prevents memory leak
const cache = createIntlCache();
const intl = createIntl( locale, messages , cache)

export const SCHEMA = 
  salutation: 
    label: intl.formatMessage(formMessages.salutationLabel),
    errormessages: 
      required: intl.formatMessage(formMessages.salutationError),
    ,
  ,
  academic_title_code: 
    label: intl.formatMessage(formMessages.academicTitleLabel),
  ,
;

【讨论】:

在新版本的 react-intl 中,此解决方案会导致错误为 .getChildContext is not a function 我每月更新一次我的库,下周我这样做时可能会遇到这个问题。但上次我检查(上个月)时,2.9.0 可以工作。感谢您指出问题。一旦我更新它,我会密切关注它,因为我经常使用它,它可能会破坏我的应用程序。【参考方案2】:

createIntl 有一种新方法可以很容易地做到这一点,它返回一个可以在 React 组件之外使用的对象。这是来自documentation 的示例。

import createIntl, createIntlCache, RawIntlProvider from 'react-intl'

// This is optional but highly recommended
// since it prevents memory leak
const cache = createIntlCache()

const intl = createIntl(
  locale: 'fr-FR',
  messages: 
, cache)

// Call imperatively
intl.formatNumber(20)

// Pass it to IntlProvider
<RawIntlProvider value=intl>foo</RawIntlProvider>

我个人将 intl 对象存储在 Redux 存储中,因此我可以在我的应用程序的任何地方访问它。

【讨论】:

它不会影响您应用程序的包大小吗? 您实际上无法在运行时通过存储或本地存储甚至按钮单击来更新它。需要将其与某物挂钩以使其具有动态性。【参考方案3】:

这一行:const intl = this.context.intl; 应该是const intl = this.context;

这是一个与您做几乎完全相同的事情的人的参考帖子:https://github.com/yahoo/react-intl/issues/983#issuecomment-342314143

在上面,作者本质上是在创建一个导出的单例,而不是像上面那样每次都创建一个新实例。这可能也是您要考虑的事情。

【讨论】:

【参考方案4】:

还有另一种方法可以解决与使用 react-intl formatMessage 处理非组件类似的问题。

创建一个 LocaleStore.js 商店文件。

 import _formatMessage from "format-message";

    export default class LocaleStore 

      formatMessage = (id, values) => 
        if (!(id in this.messages)) 
          console.warn("Id not found in intl list: " + id);
          return id;
        
        return _formatMessage(this.messages[id], values);
      ;
    

导入 LocaleStore 您的 CombinedStores.js

import LocaleStore from "./stores/LocaleStore";
import en from "./translations/en";
import de from "./translations/de";
import Global from "./stores/global"
const locale = new LocaleStore("en", 
  en,
  de
);
export default 
global:new Global(locale) 

现在您可以在 GlobalStore.js

中使用它
    class GlobalStore 
    constructor(locale) 
     this.locale = locale;
    

   formatMessage=(message_is,formatLanguage="en")=> 
     return  this.locale.formatMessage(message_id, formatLanguage);
    
  

【讨论】:

【参考方案5】:

如果您可以接受使用功能组件,我更喜欢使用useIntl hook

https://reactjs.org/docs/components-and-props.html#function-and-class-components

然后我可以得到这样的值:

import  useIntl  from "react-intl";

const intl = useIntl()
intl.formatMessage( id: 'myId' ),

https://formatjs.io/docs/react-intl/api/#useintl-hook

【讨论】:

如果您投反对票,请说出原因!否则很难改善答案。 它不能解决问题。只能在 react 组件内部使用 @LabLab 非常正确,我将通过澄清更新我的答案。

以上是关于非组件的 React-intl的主要内容,如果未能解决你的问题,请参考以下文章

将 react-intl 对象注入到已安装的 Enzyme 组件中进行测试

使用 react-intl 组件/字符串国际化

React-intl 在 Safari v8.0.8 上禁用 react-router 的 Link 组件 onClick 事件

react-intl v4升级`IntlProvider`未找到阻止组件在使用`injectIntl​​`包装器时显示

react-intl 实现 React 国际化多语言

如何处理 React-Intl 消息的多次使用?