【问题标题】:How to render a list of formatted messages using React-Intl如何使用 React-Intl 呈现格式化消息列表
【发布时间】:2019-03-15 15:54:09
【问题描述】:

我有一个navigation,其中包含以下标准项目:联系人、服务、价格等...我将其呈现如下:

const menuItemList = menuItems.map((item, index) => {
    return (
        <li key={index}>
            <NavLink to={item.url}>{item.title}</NavLink>
        </li>
    );
});

它工作正常。但现在我需要翻译这个导航,为此我使用react-intl 库。根据react-intldoc,我必须像这样使用FormattedMessage

<p>
   <FormattedMessage id="mainText"/>
</p> 

它有效。但是我如何将它用于列表渲染?我认为它可以解决这个问题,但事实并非如此。

const menuItemsList = menuItems.map((item, index) => {
    return (
        <li key={index}>
            <NavLink to={item.url}>
                <FormattedMessage id="mainText" values={item.title}/>
            </NavLink>
        </li>
    );
});

伙计们,请帮忙。如何使用来自react-intlFormattedMessage 呈现包含React 中的项目的列表?

【问题讨论】:

  • 你做错的第一件事是你传递了 values 属性。使用声明式 api,您需要为每个组件消息设置一个 id。每个标签都应该是唯一的。您应该做的是更改ID。您的本地化文件应该包含键值字符串,即内容为 { "app.title": "Hello!"} 的 en.json 文件。然后假设您有一个内容为 { "app.title": "Hallo!"} 的 de.json 文件。然后,当您在 NavLink 菜单中进行迭代时,您必须在 id 属性中传递“app.title”。
  • 包装应用程序的 intlProvider 将负责加载本地化消息并根据环境呈现消息,因此当您调用声明性 api ( 时,您只需要关心在里面有正确的ID。)

标签: javascript reactjs react-intl


【解决方案1】:

您需要将消息传递给 intl。例如:

{
    profile: 'Profile',
    settings: 'Settings'
}

另外,我想你有

const menuItems = [
    {
        url: '/profile',
        title: 'profile'
    },
    {
        url: '/settings',
        title: 'settings'
    }
] 

所以你可以这样使用它

const menuItemsList = menuItems.map((item, index) => {
    return (
        <li key={index}>
            <NavLink to={item.url}>
                <FormattedMessage id={item.title} />
            </NavLink>
        </li>
    );
});

【讨论】:

  • 这不是我要找的。我想在 en.js 语言环境文件中有一个数组并遍历它。在您的解决方案中,我必须为每个单词手动定义一个 ID。短导航很好,但是如果我在 en.js 和 ru.js 中有 400 个项目的列表怎么办?我只使用数组而不是obj。有可能吗?
  • 嗯,你可以使用ICU select。但这对您没有帮助,因为您仍然需要 item.title 的唯一 ID。因此,最好将其用作消息 ID,而不是消息值
【解决方案2】:

我最近遇到了同样的问题。 React Intl 不允许将数组传递给 &lt;FormattedMessage /&gt;,因此假设您有大量翻译项目,例如这个英语消息对象:

{
    "page.title": "Test Page",
    "page.menuItems": ["First english Item", "second english Item", ... , "100th english item"]
}

您需要一种解决方法。我们的想法是我们希望通过嵌套索引而不是全局 id 来访问这些项目。

现在我找到了两种解决方案来解决这个问题:

  1. 通过索引直接访问intl消息中的嵌套消息(hacky & fast):
import React from "react"
import {useIntl} from 'react-intl'
import NavLink from 'somewhere'

const Menu = ({menuItems}) => {
    const intl = useIntl();
    const menuItemsIntl = intl.messages["page.menuItems"]; 
    return (<div>
        {menuItems.map((item, index) => 
        <li key={index}>
            <NavLink to={item.url}>
                {menuItemsIntl[index]}
            </NavLink>
        </li>}
   </div>);
});

export default Menu;

这有一个缺点,即你没有 React Intl 的后备功能,以防你没有可用的翻译。您需要围绕 menuItemsIntl[index] 编写自定义的 FormatMessage 组件来处理丢失的键。

  1. 在将消息对象注入提供程序之前将其展平,然后通过计算出的唯一键访问它(安全且健壮):

我们需要消息对象如下所示:

{
    "page.title": "Test Page",
    "page.menuItems.1": "First english Item", 
    "page.menuItems.2": "Second english Item", 
    ...
    "page.menuItems.100": "100th english item"
}

为了实现这一点,我们需要展平原始消息对象,例如使用以下变异函数:

import translations from "./translations.json";

const Root = ()=> {
  const [lan, setLan]= useState("en");
  const messages = translations[lan]
  const flattenMessages = (obj, parKey = "") => {
    Object.keys(obj).forEach((key) => {
      const currKey = parKey + (parKey.length > 0 ? "." : "") + key;
      if (Array.isArray(obj[key]) || typeof obj[key] === 'object') {
        flattenMessages (obj[key], currKey)
      } else if (typeof obj[key] === "string") {
        messages[currKey] = obj[key]
      }
    })
  }
  flattenMessages({...messages})

  return <IntlProvider 
    key={lan}
    messages={messages}
    locale={lan} 
    defaultLocale="en"
    >
       <App />
  </IntlProvider>
};

稍后你只需使用 FormattedMessage 和你习惯的 id。您只需要根据 jsx 中的迭代器计算 id 字符串。

const Menu = ({menuItems}) => {
    return (<div>
        {menuItems.map((item, index) => 
        <li key={index}>
            <NavLink to={item.url}>
                <FormattedMessage id={`page.menuItems.${index}.title`} />
            </NavLink>
        </li>}
   </div>);
});

我编写了第一个解决方案,以防有人不知道您可以直接访问消息对象。但是,对于大多数情况,我会推荐第二种解决方案。

【讨论】:

    【解决方案3】:

    您可以使用defineMessages

    const menuItemsList = menuItems.map((item, index) => {
      return (
        <li key={index}>
          <NavLink to={item.url}>
            <FormattedMessage {...MSG[`item_${index}`]} />
          </NavLink>
        </li>
      );
    });
    
    const MSG = defineMessages({
      item_0: {
        id: 'items.service',
        defaultMessage: 'Service'
      },
      item_1: {
        id: 'items.contact',
        defaultMessage: 'Contact'
      }
    })
    

    【讨论】:

      猜你喜欢
      • 2017-04-29
      • 2020-01-19
      • 2018-02-05
      • 1970-01-01
      • 2017-10-05
      • 2016-04-24
      • 1970-01-01
      • 2017-02-11
      • 2018-02-16
      相关资源
      最近更新 更多