【问题标题】:Use React-intl translated messages in Redux middleware在 Redux 中间件中使用 React-intl 翻译的消息
【发布时间】:2016-08-07 12:41:36
【问题描述】:

我在我的应用程序中支持多种语言并为此使用 React-intl。 我有 Redux 中间件,我在其中调用服务器,如果出现错误,我想在 UI 上显示错误。

我知道我可以这样做:

1) 使用消息键从中间件分派一个动作:

{type: SHOW_ERROR, message: 'message_error_key'}

2) 在我的 React 组件中使用:

<FormattedMessage id={this.props.message_error_key}/>

但是有没有办法通过中间件已经翻译的消息来调度一个动作?

{type: SHOW_ERROR, message: [translated_message_should_be_here]}

【问题讨论】:

    标签: reactjs redux react-redux redux-framework react-intl


    【解决方案1】:

    我认为你不能直接从中间件访问formatMessage,因为它似乎只通过injectIntl 暴露给组件。您可能可以提交一个问题来描述您的用例,并且可能会考虑使用纯 JavaScript API 来访问组件外部的formatMessage(),但现在似乎不可用。

    【讨论】:

      【解决方案2】:

      在尝试将 reducer 的默认状态初始化为本地化消息时,我遇到了类似的问题。似乎在组件之外使用 react-intl 的任何部分都不是 API 中考虑的。两个想法:

      1. intl 注入到&lt;IntlProvider&gt; 下方的自定义组件中,使其通过应用程序范围的单例在componentWillReceiveProps 中可用。接下来从其他地方访问该单身人士并使用intl.formatMessage 和其他人。

      2. 可以使用 React-intl 所属的 Format.js 组件来实现所需的功能。在这种情况下,可以考虑yahoo/intl-messageformatyahoo/intl-format-cache。这当然不能很好地与开箱即用的 react-intl 集成。

      【讨论】:

        【解决方案3】:

        这可能不是最漂亮的解决方案,但我们是这样解决这个问题的;

        1) 首先,我们创建了一个“IntlGlobalProvider”组件,它从组件树中的 IntlProvider 继承上下文和道具;

        <ApolloProvider store={store} client={client}>
          <IntlProvider>
              <IntlGlobalProvider>
                  <Router history={history} children={routes} />
              </IntlGlobalProvider>
          </IntlProvider>
        </ApolloProvider>
        

        2)(在 IntlGlobalProvider.js 中)然后脱离上下文,我们得到我们想要的 intl 功能并通过单例公开它。

        // NPM Modules
        import { intlShape } from 'react-intl'
        
        // ======================================================
        // React intl passes the messages and format functions down the component
        // tree using the 'context' scope. the injectIntl HOC basically takes these out
        // of the context and injects them into the props of the component. To be able to 
        // import this translation functionality as a module anywhere (and not just inside react components),
        // this function inherits props & context from its parent and exports a singleton that'll 
        // expose all that shizzle.
        // ======================================================
        var INTL
        const IntlGlobalProvider = (props, context) => {
          INTL = context.intl
          return props.children
        }
        
        IntlGlobalProvider.contextTypes = {
          intl: intlShape.isRequired
        }
        
        // ======================================================
        // Class that exposes translations
        // ======================================================
        var instance
        class IntlTranslator {
          // Singleton
          constructor() {
            if (!instance) {
              instance = this;
            }
            return instance;
          }
        
          // ------------------------------------
          // Formatting Functions
          // ------------------------------------
          formatMessage (message, values) {
            return INTL.formatMessage(message, values)
          }
        }
        
        export const intl = new IntlTranslator()
        export default IntlGlobalProvider
        

        3) 将其作为模块导入到任何地方

        import { defineMessages } from 'react-intl'
        import { intl } from 'modules/core/IntlGlobalProvider'
        
        const intlStrings = defineMessages({
          translation: {
            id: 'myid',
            defaultMessage: 'Hey there',
            description: 'someStuff'
          },
        
        intl.formatMessage(intlStrings.translation)
        

        【讨论】:

        【解决方案4】:

        你必须使用getChildContext()来获得intl,它有formatMessage()方法。

        1.在您的根 tsx 文件中,例如应用程序.tsx。

        import { IntlProvider, addLocaleData} from 'react-intl'
        import * as locale_en from 'react-intl/locale-data/en'
        import * as locale_zh from 'react-intl/locale-data/zh'
        
        import message_en from '@/locales/en'
        import message_zh from '@/locales/zh-CN'
        
        const messages = {
          'en': flattenMessages(message_en),
          'zh': flattenMessages(message_zh)
        }
        
        addLocaleData([...locale_en, ...locale_zh])
        
        const intlProvider = new IntlProvider({ locale: 'zh', messages: messages['zh']})
        
        // export intl
        export const { intl } = intlProvider.getChildContext()
        1. 在您的 saga 文件中。

        import { intl } from '@/App';
        
        function* handleSubmit() {
          try {
            yield someApi()
          } catch(error) {
            console.log(intl.formatMessage(error.message))
          }
        }

        在底层,IntlProvider 接收这些道具并具有类方法getChildContext

        namespace IntlProvider {
              interface Props {
                  locale?: string;
                  timeZone?: string;
                  formats?: any;
                  messages?: any;
                  defaultLocale?: string;
                  defaultFormats?: any;
                  textComponent?: any;
                  initialNow?: any;
                  onError?: (error: string) => void;
              }
          }
          
        class IntlProvider extends React.Component<IntlProvider.Props> {
              getChildContext(): {
                  intl: InjectedIntl;
              };
          }

        深入InjectedIntl 界面。你可以看到为什么 intl 实例有 formatMessage 方法。

        interface InjectedIntl {
            formatDate(value: DateSource, options?: FormattedDate.PropsBase): string;
            formatTime(value: DateSource, options?: FormattedTime.PropsBase): string;
            formatRelative(value: DateSource, options?: FormattedRelative.PropsBase & { now?: any }): string;
            formatNumber(value: number, options?: FormattedNumber.PropsBase): string;
            formatPlural(value: number, options?: FormattedPlural.Base): keyof FormattedPlural.PropsBase;
            formatMessage(messageDescriptor: FormattedMessage.MessageDescriptor, values?: {[key: string]: MessageValue}): string;
            formatHTMLMessage(messageDescriptor: FormattedMessage.MessageDescriptor, values?: {[key: string]: MessageValue}): string;
            locale: string;
            formats: any;
            messages: { [id: string]: string };
            defaultLocale: string;
            defaultFormats: any;
            now(): number;
            onError(error: string): void;
        }

        【讨论】:

        • getChildContext() 不再存在。它已被 createIntl() 取代
        【解决方案5】:

        我认为您应该避免在中间件中这样做。您可以使用已翻译的消息发送您的操作。

        const deleteUser = (id, messages) => {
           type: DELETE_USER,
           payload: {id, messages}
        }
        

        然后在您的 saga(或其他中间件)中,您可以使用此已翻译的消息。

        function* deleteUserWatcher({
          payload: { id, messages }
        }) {
          try {
            yield request.delete(`/user/${id}`);
            yield put(deleteUserSuccess(id));
            yield put(pushNotificationToStack(message.success));
        
          } catch (error) {
             yield put(pushNotificationToStack(message.error));
          }
        }
        

        然后在你的组件中你可以调度动作

        const dispatch = useDispatch();
        const { formatMessage } = useIntl();
        
        const handleDeleteUser = id => {
          dispatch(deleteUser(id, {
             success: formatMessage({
              id: "User.delete.success",
              defaultMessage: "User has been deleted"
             }),
             error: formatMessage({
              id: "User.delete.error",
              defaultMessage: "Ups. Something went wrong. Sorry :("
             }),
           } 
         ));
        }
        

        我知道这并不适合所有情况,但您可以使用这种方法涵盖大多数情况

        【讨论】:

          【解决方案6】:

          现在支持和可行在 React 生命周期之外格式化字符串。可以查看createIntl官方文档here。代码可能类似于:

          intl.js

          import { createIntl, createIntlCache } from 'react-intl';
          
          let cache;
          let intl;
          
          /**
           * Generate IntlShape object
           * @param {Object} props
           * @param {String} props.locale - User specified language
           * @param {Object} props.messages - Messages
           * @returns {Object}
           */
          const generateIntl = props => {
            if (cache) {
              cache = null;
            }
          
            cache = createIntlCache();
          
            intl = createIntl(props, cache);
            return intl;
          };
          
          export { generateIntl, intl };
          
          

          root-component.jsx

          import React from 'react';
          import { RawIntlProvider, FormattedMessage } from 'react-intl';
          import { generateIntl } from './intl';
          
          const messages = { hello: 'Hello' };
          const intlValue = generateIntl({ locale: 'en', messages });
          
          export const RootComponent = () => {
            return (
              <RawIntlProvider value={intlValue}>
                <FormattedMessage id="hello" />
              </RawIntlProvider>
            );
          };
          

          intl-consumer-script.js

          import { intl } from './intl';
          
          const translation = intl.formatMessage({ id: 'hello' });
          console.log(translation);
          

          【讨论】:

            【解决方案7】:

            Simon Somlai 上面的回答启发,这里是使用反应钩子的等效版本:

            import React from 'react';
            import { useIntl } from 'react-intl';
            
            // 'intl' service singleton reference
            let intl;
            
            export function IntlGlobalProvider({ children }) {
              intl = useIntl(); // Keep the 'intl' service reference
              return children;
            }
            
            // Getter function to expose the read-only 'intl' service
            export function appIntl() {
              return intl;
            }
            
            

            然后设置IntlGlobalProvider,如上面Simon Somlai 答案的第1 步所述。现在,在任何帮助程序/实用程序类中使用 intl 时,您可以这样做:

            import { appIntl } from 'modules/core/IntlGlobalProvider';
            
            const translation = appIntl().formatMessage({ id: 'hello' });
            console.log(translation);
            
            

            【讨论】:

              猜你喜欢
              • 2018-02-16
              • 2018-09-06
              • 2021-07-20
              • 2018-02-23
              • 1970-01-01
              • 2017-11-06
              • 2021-06-01
              • 2017-07-13
              • 2020-01-19
              相关资源
              最近更新 更多