-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Rich text formatting and translations #513
Comments
@geekyme can you explain more how this would work? |
What I have in mind is Eg.
|
This sounds related to: #455 (comment) Trying to make your example work can quickly break down, imagine if someone had: <strong>eat a <em>shoe</em></strong> |
Came across a similar problem today, maybe there's a better way to do this that I'm not seeing at the moment. If I want to "format" links in a message, the only option I see right now is passing an additional <FormattedMessage
id="messageTextId"
defaultMessage="Hello {name}, please go visit {link}"
values={{
name: <strong>{this.props.name}</strong>,
link: (
<Link to="/visit-me">
<FormattedMessage
id="messageTextID.linkText"
defaultMessage="translated link title"
/>
</Link>
),
}}
/> This works, but its quite a bit to add for each link inside a message (we have a lot of them). Maybe @ericf or someone else can shed some light on a better solution for this? I kind of imagined defining a custom format for messages IE. <FormattedMessage
defaultMessage="Hello {name}, please go visit {linkKey, link, {href}}."
values={{
name: this.props.name,
linkKey: "Text inside the link",
href: "/some-page",
}}
/> but AFAIK, you can't define a custom format which isn't a number, date, or time. |
I am also currently running into this. I like the idea of nested placeholders like <FormattedMessage
defaultMessage="Hello {name}, please go visit {somekey(Text inside the link)}."
values={{
name: this.props.name,
somekey: <Link to="/some-page">{someArbitraryPlaceholder}</Link>
}}
/> Even if it looks complicated, I think this would simplify the creation of natural messages files, where you wouldn't have to rip the link text out of the context of the parent message. And you wouldn't have to create a link builder into the template language, instead the use simply decides what the element around some text looks like. |
I needed some simple formatting in my messages so I just run it through markdown. import React, { Component, PropTypes } from 'react';
import Markdown from 'react-remarkable';
class FormattedMessageMarkdown extends Component {
render() {
const { formatMessage } = this.context.intl;
const { id, defaultMessage, values, description } = this.props;
const messageDescriptor = { id, defaultMessage, values, description };
const message = formatMessage(messageDescriptor);
return (<Markdown source={message}/>);
}
}
FormattedMessageMarkdown.contextTypes = {
intl: PropTypes.object.isRequired
};
export default FormattedMessageMarkdown; Works pretty well, highly recommended. |
Last week I had some time to start thinking about this problem. What I really want to make work is something like this: <FormattedMessage id='email.sent'>
Your <a href={message.url}>email</a> has been sent.
</FormattedMessage> For the developer,
The translator shouldn't need to worry about "email" is a hyperlink in the UI, and I don't want to limit support to just HTML tags like @kamilio's Markdown approach yields. If you're using React Router, then you have What I've landed on is using XML/JSX syntax within the ICU Message string that translators see. In my example above, it would compile to:
The To illustrate this better the following would render the same: <FormattedMessage id='email.sent'>
Your <a href='/sent'>email</a> has been sent.
</FormattedMessage> <FormattedMessage
id='email.sent'
defaultMessage='Your <x:link>email</x:link> has been sent.'
values={{
link: (email) => <a href='/sent'>{email}</a>
}}
/> This shows how
I'd also like to support just providing JSX, but that would require marking the location of the placeholder which will be replaced by the translated text, here's an example of what that could look like: <FormattedMessage
id='email.sent'
defaultMessage='Your <x:link>email</x:link> has been sent.'
values={{
link: <a href='/sent'><FormattedMessage.Placeholder/></a>
}}
/> It seems that this approach should scale up to even more complex messages like this: <FormattedMessage id='emails.sent'>
<a href='/sent'>
<FormattedPlural
value={emails.length}
one={<span>Your <b>email</b> has been sent.</span>}
other={<span>Your <b>emails</b> have been sent.</span>}
/>
</a>
</FormattedMessage> The above would be extracted using
I'm working through all the various packages to make sure this is possible to implement in a backwards-compatible way. I also plan to write up a more formal RFC soon, but anyone subscribed or looking at this thread, let me know what you think! |
Thats pretty epic @ericf ! |
I'd love for react intl to support your syntax
Internally we have implemented something similar but a less advanced that already allows us to do:
It's a use case that should be natively handled by react intl and I can't wait for what you proposed to be released |
@ericf any progress on this? It would be awesome!! |
If anyone interested, feel free to take a look at // Developer is allowed to use any component inside <Trans>.
// All valid JSX is converted to ICU message format:
<Trans>See the <a href="/more">description</a> below.</Trans>
// Under the hood babel transforms component above to this:
<Trans id="See the <0>description</0> below." components=[<a href="/more" />] /> Pros:
Cons:
I guess the babel plugin could be ported to support |
Thanks @kamilio. Your markdown method works fantastic! Here is my updated version that works with simple interpolation values (formatMessage can't use values wrapped in React components) and ES2015.
I use my own helper methods
|
@bjbrewster I guess babel-plugin-react-intl wouldn't be able to extract messages if you use factories like that. It would work if you use export const t = (message, values) =>
<FormattedMessage {...message} values={values} /> And then: import { defineMessages } from 'react-intl';
const messages = defineMessages({
requiredFieldMsg: {
id: 'form.errors.required_field',
defaultMessage: 'Field {fieldName} is required'
}
});
function SomeComponent() {
return t(messages.requiredFieldMsg, { fieldName: 'Phone Number' });
} |
I made a version of FormattedMessage which maps XML tags to React Components: |
@ericf I think that proposal looks pretty cool...would definitely be a nicer way of allowing complex messages with components nested inside the message. For example, something that compiles to this:
Where the tag could be any arbitrary React component from an outside consumer, but that component needs to very specifically wrap the "this link" text. |
As we need this functionality right now, I created this package which tries to solve this in userland: |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
Issue was closed because of inactivity. If you think this is still a valid issue, please file a new issue with additional information. |
@box we are also experimenting with React components within the messages |
parser work: ad42f1f |
This unfortunately cannot be done in the parser due to complexity in parsing XML. We'd have to embed a XML parser for this to work reliably in both DOM & Node. What other libraries do seem to be re-parsing the translated message as XML (not HTML), then do another round of formatting which might be the safest way to do it. Performance-wise it won't be great, but does provide better translation context. |
Upstream change: #124 |
|
I'm so happy to see this being included natively, thank you for the hard work @longlho! |
@longlho |
I think there's already some examples in the docs but PR's definitely
welcome! Our docs could def use some ❤️
…On Wed, Aug 7, 2019 at 5:10 AM Valery ***@***.***> wrote:
@longlho <https://github.com/longlho>
Should we add some example to documentation, or new docs are already in
progress?
I made example here
https://codesandbox.io/s/charming-swartz-6bl13
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#513>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AABQM37ZS7QWHMAAGTI47DTQDKGSDANCNFSM4CIHHMDQ>
.
|
See formatjs/formatjs#513 This is not possbile within HTMLMessages, so split the paragraphs
Issue
Say i have a string:
According to https://github.com/yahoo/react-intl/wiki/Components#formattedmessage, I need to format my text this way:
When doing string extraction with https://github.com/yahoo/babel-plugin-react-intl, I would get:
To buy a shoe, { link } and { cta }
to be translated.This isn't good because I lose the context and texts from the rich content for
link
andcta
.Suggestion
I think we should have some special delimiters to surround rich text content so translators can still translate the text as a whole:
To buy a shoe, @visit our website@ and @eat a shoe@
Thoughts?
The text was updated successfully, but these errors were encountered: