簡體   English   中英

在 Redux 中間件中使用 React-intl 翻譯的消息

[英]Use React-intl translated messages in Redux middleware

我在我的應用程序中支持多種語言並為此使用 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]}

這可能不是最漂亮的解決方案,但這是我們解決這個問題的方法;

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)

我認為您不能直接從中間件訪問formatMessage ,因為它似乎只通過injectIntl暴露給組件。 您可能可以提交一個問題來描述您的用例,並且可能會考慮使用簡單的 JavaScript API 來訪問組件外部的formatMessage() ,但現在似乎不可用。

在嘗試將減速器的默認狀態初始化為本地化消息時,我遇到了一些類似的問題。 似乎在 API 中沒有考慮在組件之外使用 react-intl 的任何部分。 兩個想法:

  1. intl注入到<IntlProvider>下面的自定義組件中,這使得它可以通過應用程序范圍的單例在componentWillReceiveProps可用。 接下來從其他地方訪問該單身人士並使用intl.formatMessage和其他人。

  2. 可以使用 React-intl 所屬的Format.js組件來實現所需的功能。 在這種情況下,可以考慮yahoo/intl-messageformatyahoo/intl-format-cache 這當然不會與開箱即用的 react-intl 很好地集成。

現在支持並可行在 React 生命周期之外格式化字符串。 您可以在此處查看createIntl官方文檔。 代碼可能類似於:

內部文件

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 };

根組件.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>
  );
};

國際消費者腳本.js

import { intl } from './intl';

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

Simon Somlai上面的回答的啟發,這里是使用 react hooks 的等效版本:

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;
}

然后按照上面Simon Somlai回答的第 1 步的說明設置IntlGlobalProvider 現在,在任何幫助程序/實用程序類中使用intl時,您可以執行以下操作:

import { appIntl } from 'modules/core/IntlGlobalProvider';

const translation = appIntl().formatMessage({ id: 'hello' });
console.log(translation);

您必須使用getChildContext()來獲取具有formatMessage()方法的intl

1.在你的根 tsx 文件中,例如 App.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. 在你的傳奇文件中。

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

IntlProviderIntlProvider接收這些道具並具有類方法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; }

我相信你應該避免在中間件中這樣做。 您可以使用已翻譯的消息發送您的操作。

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

然后在您的傳奇(或其他中間件)中,您可以使用此已翻譯的消息。

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 :("
     }),
   } 
 ));
}

我知道這並不適合所有情況,但是您可以使用這種方法涵蓋大多數情況

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM