I want an elegant solution for adding styling like bold, italic, or a color for only a part of a translation.
Example
Let say that I have this i18n translation:
'my-page.confim-customer.popup.warning': 'All customer files must be authorized by the customer himself, please be sure to invite the customer to scan this QR code.'
Now i want that the words "must be authorized by the customer himself" will be in bold and in yellow. (Design requirement)
What can I do?
Solution 1 - Split in i18n
I can simply split the translation to three parts in i18n file:
// i18n/en.js
'my-page.confim-customer.popup.warning.part1': 'All customer files'
'my-page.confim-customer.popup.warning.part2': 'must be authorized by the customer himself,'
'my-page.confim-customer.popup.warning.part3': 'please be sure to invite the customer to scan this QR code.'
and then apply the css i want only on the "part2".
// MyComponent.jsx
const first = i18n.t('my-page.confim-customer.popup.warning.part1');
const second = i18n.t('my-page.confim-customer.popup.warning.part2');
const thrid = i18n.t('my-page.confim-customer.popup.warning.part3');
return (
<>
<div>
{first}
<span className="bold yellow">{second}</span>
{thrid}
</div>
</>
);
Pros
It works
Cons
Polute the i18n files with lots of partial string for each lines.
When change is required it is a hassel to change the view and translation
Solution 2 - Split in code
I keep i18n in the same translation but I add seprator in it (let say $#$):
'my-page.confim-customer.popup.warning': 'All customer files $#$must be authorized by the customer himself,$#$ please be sure to invite the customer to scan this QR code.'
And then in my component i split it:
// MyComponent.jsx
const [first, second, thrid] = i18n.t('my-page.confim-customer.popup.warning').split('$#$')
return (
<>
<div>
{first}
<span className="bold yellow">{second}</span>
{thrid}
</div>
</>
);
Pros
It works
Offload the headache to javascirpt, keep the translation in one i18n key
Cons
Need to process the key before using it
When change is needed (let say we add a new seperator for a section to be italic), we need to alter the way we process the key and change the view to match. not dynamic.
Note
I'm looking for a vanilla solution, as I'm not working with React or a popular framework.
i18next has a special component Trans. There you can use any components inside your string like:
"test" : "All customer files <yelow>must be authorized by the customer himself,</yelow> please be sure to invite the customer to scan this QR code."
...
<Trans
i18nKey='test'
components={{ yelow: <span className="bold yellow"/> }}
/>
The strings split into separate keys are extremely not translator-friendly. It would be hard for translators to provide quality translations since they need to know where each part is used just not to break the UI.
In addition, the translation for some languages likely will have a different word order. The situation is even more complicated in RTL languages.
So, it's definitely recommended to avoid such approaches with string splitting.
As a Vanilla JS solution that can work in your case, I would recommend the Lingui library.
In short, it allows developers to do the following thing by using the #lingui/macro package:
import { Trans } from "#lingui/macro"
<Trans>Read the docs.</Trans>;
Then you can extract all your localizable texts to the .po or .json file by using the Lingui CLI.
Components and HTML tags are replaced with dummy indexed tags (<0></0>) which has several advantages:
both custom React components and built-in HTML tags are supported
change of component props doesn't break the translation
the message is extracted as a whole sentence (this seems to be obvious, but most i18n libs simply split the message into pieces by tags and translate them separately)
Further reading:
Internationalization of JavaScript apps
Working with LinguiJS CLI
#lingui/macro docs
I need to add some raw HTML to my <head> to my React/Next.js app in addition to already-existing elements, something like below:
<head>
<existingElements.../>
[I WANT TO INSERT WHATEVER ARBITRARY HTML HERE]
</head>
If I use dangerouslySetInnerHTML I lose the existing elements.
I can't use dangerouslySetInnerHTML as <Head> in Next.js doesn't have that property at all (and I'm trying to add to the Head element).
If I try using node-html-parser or react-html-parser:
import {parse} from 'node-html-parser';
[...]
let additionalElements:any = <></>;
additionalElements = parse(MY_ARBITRARY_HTML_STRING); // no parse errors here
[...]
return <head>{additionalElements}</head>
They end up erroring with `Error: Objects are not valid as a React child (my parsed HTML here).
I've thought there was a problem with the HTML that I provide but I get the same error even if I try providing something simple as <b>hello</b>.
How can I insert arbitrary HTML string into the head element while keeping existing head elements too, using React and Next.js?
(Note: I'm fully aware of XSS and security implications, this is an internally/strictly controlled HTML.)
How would one go about injecting raw HTML with Vue when that HTML contains Vue markup that needs to be evaluated?
For example, this could be used to render HTML when it comes from a variable:
<p v-html="markup"></p>
{
computed: {
markup() {
return 'Hello <em>World</em>!';
},
},
}
This will work nicely, but what if the markup computed property also included a custom Vue component?
return 'Hello <em>World</em>! My name is <my-name></my-name>!';
In this case <my-name></my-name> will not be evaluated unfortunately and just rendered as-is.
How do I go about having it "Vue-fied" and also how does scope work in that case?
I read the docs thoroughly, turns out it's not really possible "out-of-the-box" as it's somewhat of an anti-pattern.
The problem I am trying to fix however demands it as I want to be able to inject custom components in an arbitrary places in translated text.
I ended up using Custom Directives.
The only limitation here is that if you want to pass a property, say name from the context of where you're including the directive, you must reference it via $parent.
return 'Hello <em>World</em>! My name is <my-name :name="$parent.name"></my-name>!';
Usage will be:
<p v-markup="markup"></p>
When rendered, the above will be identical to doing this:
<p>
Hello <em>World</em>! My name is <my-name :name="name"></my-name>!
</p>
Checkout this Gist
I am pulling JSON data from an API. The JSON contains blog posts from social media site Steemit.com. The posts are in markup and I want to grab the markup from the API, convert it to regular HTML, and display it in a React component.
Here is my render function. The API data is called in componentDidMount and consists of deeply nested objects. Excuse the messiness.
render() {
const posts = Object.keys(this.state.myPosts);
let page = posts.map((post, i) => {
return (
this.state.myPosts[posts[i]]["category"] === "utopian-io"
?
<div className="content-container">
<p>
<strong>Author: {this.state.myPosts[posts[i]]["author"]}</strong>
</p>
<p>
<strong>Title: {this.state.myPosts[posts[i]]["title"]}</strong>
</p>
<p>
<strong>Payout: {this.state.myPosts[posts[i]]["total_payout_value"]}</strong>
</p>
<p>{this.state.myPosts[posts[i]]["created"]}</p>
<p><span>Category: </span>{this.state.myPosts[posts[i]]["category"]}</p>
// Successfully renders the markup, but I want to convert this to HTML so it displays as HTML
<p>{this.state.myPosts[posts[i]]["body"]}</p>
// I have tried the following but I get a "not a string" console.error or the browser just crashes
<Markdown>{this.state.myPosts[posts[i]]["body"]}</Markdown>
</div>
:
null
);
});
return (
<div>
{page}
</div>
);
}
}
The Markdown-to-JSX package is successfully imported and calling it with the <Markdown></Markdown> tags is the appropriate way to use it. The problem is it expects a string and when I pass it this.state.myPosts[posts[i]]["body"] it won't work. I have tried to convert the data to a string using toString() and JSON.stringify() but I am unable to figure this out.
How do I pass the JSON markup data from the API to Markdown-to-JSX as a string?
In order words, I think converting this.state.myPosts[posts[i]]["body"] to a string should fix this but I don't know how to do this.
Option 1: dangerouslySetInnerHTML
If you would like to inject the HTML in the body of the post, use dangerouslySetInnerHTML
render: function() {
return (
<div dangerouslySetInnerHTML={{__html: this.state.myPosts[posts[i]]["body"]}></div>
);
}
You need to use dangerouslySetInnerHTML because React escapes the HTML to prevent XSS ( a security stuff to avoid injecting malicious code )
If you transform the post body to string directly, you will add in the body a lot of html tags as string, not recommended either.
If you parse the markup, and would like to convert it, its a heavy task.
In general, the practice for this is that the API call wouldn't return you a formatted markup.
Option 2: npm-library
https://www.npmjs.com/package/html-to-react
You shouldn't loose the magic of React. So this probably is a nice option to convert your post patterns into React 'Post Body' Components.
Your result should be having all inside components. In this way you are keeping the benefits of React
I'm using React and created a small page that has 4 components (React classes, what is the preferred term? I'll call them components in this post):
Component Breakdown
a parent "App" component that includes and manages the other components
a "Form" component that lets the user interact with the page
a "String View" component that displays the input from the form as text
a "Visual View" (I know, bad name...) component that interprets the string view and performs actions to adjust the visual.
Dataflow
The communication of these components using states and props is as follows:
The Form has onChange handlers that pass the new state to the App
The App funnels the state data to the String View
The String View updates and passes the updated state to the App
The App funnels the new state data to the Visual View
Finally, the Visual View now updates based on the new state.
Sample Code
var App = React.createClass({
handleFormChange: function(formData) {
this.setState({formData:formData});
},
handleStringChange: function(stringData) {
this.setState({stringData:stringData});
},
render: function() {
return (
<div className="app">
<FormView onFormChange={this.handleFormChange}/>
<StringView formData={this.state.formData} onStringChange={this.handleStringChange}/>
<VisualView stringData={this.state.stringData}/>
</div>
);
}
});
var FormView = React.createClass({
handleFormChange: function(e) {
this.props.onFormChange(e.target.value);
}
render: function() {
return(
<div className="formView">
<select onChange={this.handleFormChange}>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
</select>
</div>
);
}
});
var StringView = React.createClass({
componentDidUpdate: function() {
this.props.onStringChange({newString:'newStringState'});
},
render: function() {
this.props.formData;
// process formData and update state
return (
<div className="stringView">
{this.props.formData}
</div>
);
}
});
var VisualView = React.createClass({
render: function() {
var selection = this.props.stringData,
output = '';
if (selection === 1) {
output = 'Hooray, 1!';
} else {
output = 'Yes! 2!';
}
return (
<div className="stringView">
{output}
</div>
);
}
});
Questions
Is this the correct dataflow paradigm that React is trying to enforce (components only talk to parents, not siblings)?
Compared to how I would have written this in just regular JavaScript, this seems terribly constrained. Am I missing the big picture? Is this dataflow paradigm designed to prevent future problems (if so, which ones? Any that can't be solved with disciplined regular JavaScript?), or is there some other purpose that I'm missing?
I'm getting a lot of repeated function names (handleFormChange for example, it's used in App and Form View), is there a good way to make these distinguishable? Or, are repeated function names across components desirable?
When the components actually build, the JSX stuff gets transpiled down into real JavaScript. Is there an advantage to using JSX? Would writing components in the already transpiled JavaScript have an advantage?
To start, I think it is ok to call "components", and I've seen lot of people call that way. I will answer your questions below, in an order that I think is better to make my answers make sense.
When the components actually build, the JSX stuff gets transpiled down into real JavaScript. Is there an advantage to using JSX? Would writing components in the already transpiled JavaScript have an advantage?
JSX kinda mixes JavaScript and HTML, so, it makes your code "friendly". You will create your components, and just "call" them as HTML tags. Below you can see the difference between writing JSX and pure JavaScript.
return <div className="my-component"><p>Awesome</p></div>;
return ReactDOM.div({
className: 'my-component'
}, ReactDOM.p({}, "Awesome"));
I don't know you, but I would get tired to write this amount of code just to render a div with a paragraph.
You can check more benefits of using it here:
https://hchen1202.gitbooks.io/learning-react-js/content/benefits_of_jsx.html
I'm getting a lot of repeat function names (handleFormChange for example, it's used in App and Form View), is there a good way to make these distinguishable? Or, are repeated function names across components desirable?
It is not bad, also, your app is a "demo" one, if it would be a "real" one, it would have some better names for the components (i.e. <FormView> would be <ContactForm>) and maybe your method names would be different. But it is not bad at all. For example, inside <ContactForm> you may call the submit handler as onSubmit, but outside (the prop that you pass), you may call onContactFormSubmit, or, in a more semantic way, onContactFormFilled.
If your application starts to grow and you have lots of things repeated in the same component (that is the case of your <App>), you may try to split your components, therefore, each of your component will "know" about a "domain", and it would not appear to have lots of repeated stuff.
Is this the correct dataflow paradigm that React is trying to enforce (components only talk to parents, not siblings)?
First of all, React doesn't "enforce" anything, as some people say, React is the "v" in MVC, so, you have your "presentation" layer described as components, and the data may flow in the way you want.
But you got a point when you say "components only talk to parents, not siblings", because that is the way you can "communicate" between your components when you have multiple components. Since a component can't see its sibling, you need someone to orchestrate this communication, and, in this case, this is the parent's job.
There are other ways to make components "talk" to each other (i.e. using refs), but having a parent to orchestrate is, IMO, the most reliable (and better testable) one.
Compared to how I would have written this in just regular JavaScript, this seems terribly constrained. Am I missing the big picture? Is this dataflow paradigm designed to prevent future problems (if so, which ones? Any that can't be solved with disciplined regular JavaScript?), or is there some other purpose that I'm missing?
I decided to answer that as the last one, to sum up some things.
IMO, React is just great, you start to have your "logic" in the right place (a component), and you can just compose things in order to make your page work well (and by well I mean it is orchestrated correctly).
React also makes it easier to "think" about how you will build your interfaces. This Pete Hunt's blog post is amazing, and you should check it out:
https://facebook.github.io/react/docs/thinking-in-react.html
If you would be writing your code with plain JavaScript, you would have to handle DOM in some way (i.e. using a template engine) and your code would end up mixing DOM manipulation with your application logic. React just abstracts that for you. You can only care about presenting stuff. Another advantage is that, when everything is a component, you can reuse those components, it doesn't matter where they are located. If you pass the props correctly, your component will work as expected.
I know it seems exhaustive to write those components, but as you start to write more components you start to see lots of benefits. One of them is to nevermore wonder about how to present your data (no more concatenating HTML strings or calling template functions). Another one is that it is easy to "split" your interfaces, what makes your code easier to maintain (and that is not straightforward when using plain JavaScript).
To be honest, this application you wrote is really simple, and you may not see lots of advantages of using React for building it. I think you should try to create a more "complex" one, and compare it with plain JavaScript. By "complex", I mean "user interface" complex. For example, create a form that allows user to submit multiple "people". And "people" should have "name" and multiple "pet" (which also have a name). You will see how hard is it to handle "add" and "remove" operations in this case, and how easy React handle that kind of thing.
I think that is it, I hope you and React "click". It changed my mind about how to create complex user interfaces.