React-Intl How to use FormattedMessage in input placeholder
Asked Answered
T

13

73

I'm unsure how to get the values from

<FormattedMessage {...messages.placeholderIntlText} />

into a placeholder format like input:

<input placeholder={<FormattedMessage {...messages.placeholderIntlText} />} />

as it would return [Object object] in the actual placeholder. Is there a way to get the actual correct value?

Tavares answered 22/9, 2016 at 5:0 Comment(1)
The intl and injection worked for me from this: #33442024Tavares
D
133

The <Formatted... /> React components in react-intl are meant to be used in rendering scenarios and are not meant to be used in placeholders, alternate text, etc. They render HTML, not plain text, which is not useful in your scenario.

Instead, react-intl provides a lower level API for exactly this same reason. The rendering components themselves use this API under the hoods to format the values into HTML. Your scenario probably requires you to use the lower level formatMessage(...) API.

You should inject the intl object into your component by using the injectIntl HOC and then just format the message through the API.

Example:

import React from 'react';
import { injectIntl, intlShape } from 'react-intl';

const ChildComponent = ({ intl }) => {
  const placeholder = intl.formatMessage({id: 'messageId'});
  return(
     <input placeholder={placeholder} />
  );
}

ChildComponent.propTypes = {
  intl: intlShape.isRequired
}

export default injectIntl(ChildComponent);

Please note that I'm using some ES6 features here, so adapt according to your setup.

Defection answered 28/9, 2016 at 16:45 Comment(2)
Yeah this response is good, but, I still think the purpose of this HoC is very confusing. const ChildComponent = (props, context) => { and context.intl.formatMessage() do exactly the same thing and you don't need this HoC. I don't understand why all the responses here are not even suggesting this.Valine
@NicolasRenon, this answer is two and half years old. By now I would assume other/better options are available. Still, this particular example is of no particular interest but in a bigger scope ChildComponent could have any other kind of logic you'd like (formatting of many messages, for instance). It's not meant to be a one size fits all solution. It's just an example.Defection
C
41
  • You can use intl prop from injectIntl HoC
  • You can also provide function as child component:
<FormattedMessage {...messages.placeholderIntlText}>
  {(msg) => (<input placeholder={msg} />)}
</FormattedMessage>
Convenance answered 12/8, 2017 at 0:8 Comment(3)
this one is easier to applyPestle
@Pestle No, it is not. This one blots the source code with unnecessary <FormattedMessage/> tags.Portie
@KeremBaydoğan I write down both possibilities. Depends on case. If you are rendering DOM element and you don't want wrap it in <span>, you should use second example.Convenance
H
21

It's july 2019 and react-intl 3 beta is shipped with a useIntl hook to make these kind of translations easier:

import React from 'react';
import {useIntl, FormattedDate} from 'react-intl';

const FunctionComponent: React.FC<{date: number | Date}> = ({date}) => {
  const intl = useIntl();
  return (
    <span title={intl.formatDate(date)}>
      <FormattedDate value={date} />
    </span>
  );
};

export default FunctionComponent;

And then you can make custom hooks to use the methods provided by the API:

import { useIntl } from 'react-intl'

export function useFormatMessage(messageId) {
  return useIntl().formatMessage({ id: messageId })
}
Howdy answered 19/7, 2019 at 15:14 Comment(1)
2023 here from the future: can't use hooks in functions: React Hook "useIntl" is called in function "useFormatMessage" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use".eslintreact-hooks/rules-of-hooksXanthic
J
17

For Input placeholder for more details

   <FormattedMessage id="yourid" defaultMessage="search">
    {placeholder=>  
        <Input placeholder={placeholder}/>
        }
    </FormattedMessage>
Janae answered 24/11, 2018 at 17:21 Comment(4)
IMO this is better than the injection way.Immunity
This is the best solution on the page and the only one to solve my problem, as id="yourid" was requiredOlethea
This method works for me as well. The simplest way over there to solve the problem.Candelaria
I got the Type 'ReactNode[]' is not assignable to type 'string' in TSLutero
S
10

As from React version >= 16.8, you can use useIntl hook :

import React from 'react';
import { IntlProvider, useIntl } from 'react-intl';

const FunctionComponent = () => {
    const intl = useIntl();
    const lang = "en";
    const messages = {
      en: {
        'placeholderMessageId': 'placeholder in english',
      },
      fr: {
        'placeholderMessageId': 'placeholder en fançais',
      }
    }
    return ( 
      <IntlProvider locale = {lang} messages = { messages[lang] } >
          <input placeholder = { intl.formatMessage({ id: 'placeholderMessageId' })}/> 
      </IntlProvider >
      );
    };

export default FunctionComponent;
Sturdy answered 13/11, 2019 at 8:58 Comment(2)
How about non-functional component ? I tried your solution but I get hook errorNarah
You may wanna check the first answer using injectIntlSturdy
B
5

I would like to suggest this solution :

import { useIntl } from "react-intl";

export default function MyComponent() {
  const intl = useIntl();

  return (
    <input placeholder={intl.formatMessage({ id: "messageId" })} />
  );
}
Bambi answered 30/3, 2022 at 8:14 Comment(1)
Beautiful! Exactly what I was looking for.Xerxes
S
4

Based on the react intl wiki the implementation of an input box with translatable placeholder will look like:

import React from 'react';
import { injectIntl, intlShape, defineMessages } from 'react-intl';

const messages = defineMessages({
  placeholder: {
    id: 'myPlaceholderText',
    defaultMessage: '{text} and static text',
  },
});

const ComponentWithInput = ({ intl, placeholderText }) => {
  return (
    <input
      placeholder={ intl.formatMessage(messages.placeholder, { text: placeholderText }) }
    />
  );
};

ComponentWithInput.propTypes = {
  intl: intlShape.isRequired
};

export default injectIntl(ComponentWithInput);

and the useage of it:

import ComponentWithInput from './component-with-input';
...
render() {
  <ComponentWithInput placeholderText="foo" />
}
...

The id: 'myPlaceholderText', part is necessary to enable the babel-plugin-react-intl to collect the messages for translation.

Strauss answered 19/1, 2018 at 13:34 Comment(1)
Really nice example !Andiron
H
1

You are trying to render a React component named FormattedMessage into a placeholder tag which is expecting a string.

You should instead just create a function named FormattedMessage that returns a string into the placeholder.

function FormattedMessage(props) {
    ...
}

<input placeholder=`{$(FormattedMessage({...messages.placeholderIntlText})}` />
Homocyclic answered 22/9, 2016 at 7:56 Comment(1)
Bryan is probably asking about react-intl, see #35186797Sterrett
P
1

Consider this possibility.

The simplest solution

<IntlMessages id="category.name">
    {text => (
        <Input placeholder={text} />
    )}
</IntlMessages>

OR

enter image description here

Papilionaceous answered 30/7, 2020 at 13:47 Comment(1)
Please don't post your code in a picture. Use codeboxes.Repugnance
R
0

In my case I had the whole app in one file, so using export wouldn't work. This one uses the normal class structure so you can use the state and other functionality of React if needed.

class nameInputOrig extends React.Component {
  render () {
    const {formatMessage} = this.props.intl;
    return (
        <input type="text" placeholder={formatMessage({id:"placeholderIntlText"})} />
    );
  }
}

const nameInput = injectIntl(nameInputOrig);

Apply using the created constant:

class App extends React.Component {
    render () {
        <nameInput />
    }
}
Rembrandt answered 30/12, 2017 at 17:10 Comment(0)
A
0

Starting from the @gazdagerg 's answer, I have adapted his code in order to:

  • having a new component that is a wrapper over an input
  • receives an ID of a string from locale conf
  • based on the ID, it returns the string in respect to the global locale setting
  • handling the situation when the string ID is not set (this caused exception and page to crash)

    import React from 'react';
    import { injectIntl, intlShape, defineMessages } from 'react-intl';


    const InputWithPlaceholder = ({ intl, placeholder }) => {

      const messages = defineMessages({
        placeholder: {
          id: placeholder,
          defaultMessage: '',
        },
      });

      if(messages.placeholder.id) {
        return (
          <input placeholder={ intl.formatMessage(messages.placeholder) } />
        );
      } else {
        return (
          <input/>
        );
      }
    };

    InputWithPlaceholder.propTypes = {
      intl: intlShape.isRequired
    };

    export default injectIntl(InputWithPlaceholder);

You can use it in other file by:

  1. import the new component
  2. use it with the ID of the locale string as parameter
import InputWithIntlPlaceholder from 'your/path/to/component/InputWithIntlPlaceholder';

... more code here ...

<InputWithIntlPlaceholder placeholder="your.locale.string.id" />
Andiron answered 26/7, 2019 at 10:27 Comment(0)
H
0

react-intl Imperative API.

import {useIntl} from "react-intl";

export default function Input() {
    const translate = (key) => useIntl().formatMessage({id: key})

    return <input placeholder={translate('your-translation-key')}/>
}

You can outsource translate() as you whish.

Heer answered 9/2 at 9:2 Comment(0)
N
-1

Like this:

import React, {PropTypes} from 'react';
import { injectIntl, FormattedMessage } from 'react-intl';
 
/**
* {
* "hello": "Hello",
* "world": "World"
* }
*/
 
// pure function
const PureFunciton = injectIntl(({ intl }) => {
return (
  <div>
    <p>{intl.formatMessage({ id: 'hello' })}</p>
    <p><FormattedMessage id="world" /></p>
  </div>
)
});
 
// class Component
class componentName extends Component {
  handleStr = () => {
    // return 'Hello';
    const { intl } = this.props;
    return intl.formatMessage({ id: 'hello' })
  }
  render() {
    return (
      <div>
        <p>{this.handleStr()}</p>
        <p><FormattedMessage id="world" /></p>
      </div>
    );
  }
}
 
export default injectIntl(connect(componentName));
Nuncle answered 21/11, 2017 at 11:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.