Skip to content
This repository has been archived by the owner on Jun 8, 2019. It is now read-only.

Cannot use backslash('\') in jsx value string #13

Closed
ikhilko opened this issue Oct 26, 2015 · 9 comments
Closed

Cannot use backslash('\') in jsx value string #13

ikhilko opened this issue Oct 26, 2015 · 9 comments

Comments

@ikhilko
Copy link

ikhilko commented Oct 26, 2015

When I'm trying to use backslash in my values (defaultMessage) in JSX, like this:

<FormattedMessage id="someId" defaultMessage="My message (one\\two\\three)"/> 

Plugin raises:

Message failed to parse: SyntaxError: Expected "\\#", "\\u", "\\{", "\\}", "{", [^{}\\\0-\x1F� \t\n\r], end of input or whitespace but "\\" found. See: http://formatjs.io/guides/message-syntax/

So it's looks like I cannot use \ symbol in JSX value. Any suggestions?

@ericf
Copy link
Collaborator

ericf commented Oct 26, 2015

It's non-obvious because of how the string is first processed, but you need four backslashes:

<FormattedMessage id="someId" defaultMessage="My message (one\\\\two\\\\three)"/> 

That said, I believe there is a bug here that I can fix in this plugin. The default message strings will be processed as part of the extraction process and end up with the following value in the JSON file:

[
  {
    "id": "someId",
    "defaultMessage": "My message (one\\two\\three)"
  }
]

While the message left in the source file will still contain the four backslashes. This could mean that when falling back to the default message in source (because the translation wasn't loaded or passed in to <IntlProvider>), the result in the UI would contain double backslashes, instead of the desired single backslash. Whether or not this is the case is in how Babel handles the files during transpilation. If Babel leaves the original string in the source with the four backslashes, then I'll update the plugin to fix that.

@ericf
Copy link
Collaborator

ericf commented Oct 26, 2015

I also realized that I haven't released intl-messageformat-parser which supports backslashes… I'll ping this thread again when I have.

formatjs/intl-messageformat-parser@v1.1.0...master

@ericf ericf closed this as completed in 7a3b57b Oct 27, 2015
@ericf
Copy link
Collaborator

ericf commented Oct 27, 2015

@ikhilko Thanks again for pointing this out. I spent a good part of the day revamping how backslash-escaping works for extracting messages, and it's now corrected. I've released intl-messageformat-parser@1.2.0 which adds support for escaping backslashes themselves. I will work on releasing an updated version of this plugin and React Intl v2 tomorrow.

@ericf
Copy link
Collaborator

ericf commented Oct 27, 2015

babel-plugin-react-intl@1.0.0-beta-5 has been released which fixes this escaping issue.

@ikhilko
Copy link
Author

ikhilko commented Oct 28, 2015

Sounds good! Thank you

@ikhilko
Copy link
Author

ikhilko commented Oct 28, 2015

@ericf, i have a question again... I'm just checked out babel-plugin-react-intl@1.0.0-beta-5 and try it. And again a have a question how to use backslashes in my sources.. If I want a single backslash in my message what I need to write in JSX?

<FormattedMessage id="someId" defaultMessage={"one \ two"}/>  escapes space symbol
<FormattedMessage id="someId" defaultMessage={"one \\ two"}/> error in this plugin
<FormattedMessage id="someId" defaultMessage={"one \\\ two"}/> error in this plugin
<FormattedMessage id="someId" defaultMessage={"one \\\\ two"}/> \\\\ transformed to \\\\ in json

and again

<FormattedMessage id="someId" defaultMessage="one \ two"/>  escapes space symbol
<FormattedMessage id="someId" defaultMessage="one \\ two"/> \\ transformed to \\\\ in json
<FormattedMessage id="someId" defaultMessage="one \\\ two"/> error in this plugin
<FormattedMessage id="someId" defaultMessage="one \\\\ two"/> \\\\ transformed to \\\\\\\\ in json

My expectations was that plugin will do it like JS do.
No matter I use {""} or "" syntax:

"one \ two" transformed to json as is "one \ two" (escape space)
"one \\ two" transformed to json as is "one \\ two" (single backslash)
"one \\\ two" transformed to json as is "one \\\ two" (single backslash)
"one \\\\ two" transformed to json as is "one \\\\ two" (double backslash)

Please, can you correct me?

@ericf
Copy link
Collaborator

ericf commented Oct 28, 2015

@ikhilko intl-messageformat-parser requires double backslashes to escape things, otherwise the escaping intent would be lost in translation (sorry I couldn't help myself), and needs this to escape ICU Message syntax characters.

In JavaScript strings: "\{" === "{", so we need a way to express the intent that you want the curly brace (which is a ICU Message syntax character) to not be interpreted when parsing. That's why the double backslash is required: "\\{"

This then extends into how to tell the parser not to interpret "\\" as the beginning of an escape sequence so a literal backslash character can be displayed. To do this we need to double-escape the backslashes which is why "\\\\" is interpreted by the parser as "\\" which when rendered will display: \

Strings defined in JSX are parsed like HTML/XML strings, not JavaScript strings; this means they are double-escaped. Depending on which string you're creating (JavaScript or JSX) you need a different amount of backslashes.

Using JSX Strings:

<FormattedMessage id="someId" defaultMessage="one \\ two" />

When the JSX string "one \\ two" is parsed, it's value as a JavaScript string is actually: "one \\\\ two" and therefore can be properly parsed by intl-messageformat-parser. If this JSX string wasn't going to be parsed as an ICU Message, then you could define it as: "one \ two".

Using JavaScript Strings:

<FormattedMessage id="someId" defaultMessage={"one \\\\ two"} />

I hope this makes some sense as to why we're doing escape sequences this way for ICU Message strings. Also I just released an updated React Intl which uses the latest version of intl-messageformat-parser that now supports escaping backslashes themselves. Update to react-intl@2.0.0-beta-1.

@ikhilko
Copy link
Author

ikhilko commented Oct 29, 2015

Thanks, you for detailed explanation of how it work. Now it more clear for me.

@oatkiller
Copy link

@ericf Thank you very much for your answer. I am having a problem that may be related:

I have posted a question on stackoverflow here with the details. I have inlined the question into this comment as well. Thank you in advance for any assistance.

http://stackoverflow.com/questions/42843637/rendering-a-backslash-from-react-intl-message-when-building-with-webpack-1

Summary

When I load my app using Webpack Dev Server, my messages show up fine:
\. However when I bundle the application to disk and server the
bundle via nginx, I see a duplication of my backslash: \\.

Details

I am using react-intl@2.2.3 (latest at this time) and babel-plugin-react-intl@2.3.1 (latest at this time.) My goal is to define a default message with a \ character and render it in any way (FormattedMessage, formatMessage, etc)

I am bundling my app using Webpack / Babel. I have no Babel / react-intl specific config in my webpack.config.js file, however I do use DefinePlugin to set process.env to 'development' or 'production'

When I load my app using Webpack Dev Server, my messages show up fine: \. However when I bundle the application to disk and serve the bundle via nginx, I see a duplication of my backslash: \\. I have following instructions here: #13 (comment) with regard to using 4 \ characters to show a final \ character.

For what it is worth, I have tried using JSX strings, JS strings, using 1, 2, and 4 \ characters, and any other silly combinations I could think of.

Any tips or suggestions are greatly appreciated. Thank you.

Code samples

An example of how I define messages

import { defineMessages } from 'react-intl'


export default defineMessages({
  message: {
    id: 'anyId',
    defaultMessage: '\\\\',
  },
})

An example of how I might render my messages

<FormattedMessage { ...messages.anyID } /></span>

Another example, which also does not work

<FormattedMessage id='anyId' defaultMessage='\\' />

Yet another failing example:

<FormattedMessage id='anyId' defaultMessage={ '\\\\' } />

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants