Skip to content
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

Allow React components as dynamic data arguments #100

Merged
merged 2 commits into from
Jun 25, 2018

Conversation

thchia
Copy link
Collaborator

@thchia thchia commented Jun 23, 2018

From #76.

Repeating the summary for posterity.

Before

templater() would return a string with all the dynamic content filled in:

const translation = 'Hello ${ name }'
const data = { name: 'Google' }

templater(translation, data) // 'Hello Google'

getLocalizedElement would check if the result of templater() has any HTML tags in it:

const getLocalizedElement = (...args) => {

  ...

  const translatedValues = templater(translations, data)

  return renderInnerHtml && hasHtmlTags(translatedValues)
  ?  React.createElement('span', { dangerouslySetInnerHTML: { __html: translatedValues } })
  : translatedValues

}

After

templater() returns a string if the dynamic content does not contain any values satisfying React.isValidElement(). Otherwise, it returns an array of string/React components:

const translation = 'Hello ${name}'
const data = { name: 'Google' }

templater(translation, data) // 'Hello Google'

...

const translation = 'Hello ${name}'
const Component = () => <strong>Google</strong>
const data = { name: <Component /> }

templater(translation, data) // ['Hello', <strong>Google</strong> ]

getLocalizedElement checks if the result of templater is a string or not. If it is, it does the same as before. If it is not (i.e. we are passing React components as dynamic content, and it is an array), first check that no string element in the array satisfies hasHtmlTags() (see below). If that check passes, spread the array into React.createElement:

// Pseudocode

const getLocalizedElement = (...args) => {
  
  ...
  
  const translatedValues = templater(translations, data)

  if (typeof translatedValues === 'string') {
    // return same as before
  }

  // check for hasHtmlTags()

  return React.createElement('span', null, ...translatedValues)
}

Known issues

  • HTML in translations: This approach does not support having HTML in the translation. The reason is that the templater will split the stringified HTML into [ '<span>', <strong>Google</strong>, '</span>' ] and I'm not sure how to re-form this into HTML. Using dangerouslySetInnerHTML will lose the <strong>Google</strong> (because it is not a string), but is the current way of parsing the string HTML.

  • React Native: Any translations with React components in data are returned as React.createElement('span', ...). This won't work in RN. I'm less familiar with RN so wonder if replacing span with Fragment would work.

@codecov
Copy link

codecov bot commented Jun 23, 2018

Codecov Report

Merging #100 into master will increase coverage by 2.12%.
The diff coverage is 100%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #100      +/-   ##
==========================================
+ Coverage   93.21%   95.33%   +2.12%     
==========================================
  Files           6        6              
  Lines         221      236      +15     
  Branches       61       68       +7     
==========================================
+ Hits          206      225      +19     
+ Misses         13       10       -3     
+ Partials        2        1       -1
Impacted Files Coverage Δ
src/localize.js 96.51% <ø> (ø) ⬆️
src/utils.js 100% <100%> (+6.25%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update c8358d9...3bf381c. Read the comment docs.

@colbyfayock
Copy link

hey @thchia did you try something like this for the HTML issue?

[ '<span>', <strong>Google</strong>, '</span>' ].map(tag => String(tag))

Or is the <strong> considered a component in that array?

@thchia
Copy link
Collaborator Author

thchia commented Jun 23, 2018

@colbyfayock the second element is considered a component.

There may be some way to do it with react-dom's renderToString()?

@ryandrewjohnson
Copy link
Owner

@thchia @colbyfayock I'm going to publish now as is, but maybe we can add an enhancement to allow for HTML if we're able to get it working.

@ryandrewjohnson ryandrewjohnson merged commit 7b18367 into ryandrewjohnson:master Jun 25, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants