React-native-i18n: Is it possible to Interpolate objects?

Created on 20 Aug 2016  路  11Comments  路  Source: AlexanderZaytsev/react-native-i18n

Trying to use this

<Text>
          {I18n.t('login_condition', {
            appName: <Text style={{color: 'blue'}} onPress={() => Linking.openURL('http://google.com')}>Google</Text>
          })}
</Text>

Most helpful comment

I came up with a helper component that does the 'rich text interpolation'. It's written in ES5, and without semi-colon (airbnb rules).

import React from 'react'
import PropTypes from 'prop-types'
import { Text } from 'react-native'
import I18n from 'react-native-i18n'

export const RichI18n = (props) => {
  const { id, values, baseComponent, baseComponentStyle, baseComponentProps, ...other } = props
  // get the translations interpoling the placeholder by itself
  const keys = Object.keys(values)
  const placeHolders = {}
  keys.forEach((key) => placeHolders[key] = `{{${key}}}`)
  const translation = I18n.t(id, { ...(other || {}), ...placeHolders }) || ''
  // split the translated string, using default i18n-js regex, odd indexes will correlate to a key to be interpolated
  // https://github.com/fnando/i18n-js/blob/master/app/assets/javascripts/i18n.js
  const splitted = translation.split(/(?:\{\{|%\{)(.*?)(?:\}\}?)/gm)
  const interpolated = []
  splitted.forEach((str, idx) => {
    if ((idx % 2) === 0) {
      interpolated.push(str)
    } else {
      const child = values[str]
      if (typeof child === 'string') {
        interpolated.push(child)
      } else {
        interpolated.push(React.cloneElement(child, { key: idx }))
      }
    }
  })
  const BaseComponent = baseComponent
  return (
    <BaseComponent style={baseComponentStyle} {...baseComponentProps}>{interpolated}</BaseComponent>
  )
}

RichI18n.propTypes = {
  id: PropTypes.string.isRequired,
  values: PropTypes.object,
  baseComponent: PropTypes.any,
  baseComponentStyle: PropTypes.object,
  baseComponentProps: PropTypes.object
}

RichI18n.defaultProps = {
  values: {},
  baseComponent: Text,
  baseComponentStyle: {},
  baseComponentProps: {}
}

Usage:

 <RichI18n
  id="my.interpolation.test"
  defaultValue="This is an {{interpolation}} test {{plain}} where {{you}} can repeat {{interpolation}} variables {{translated}}"
  values={{
    interpolation: <Text style={{ fontWeight: 'bold' }}>bold</Text>,
    you: <Text style={{ color: 'red' }}>me</Text>,
    plain: 'Just plain text',
    translated: I18n.t('Translated text', { defaultValue: 'Translated text' })
  }}
/>

All 11 comments

I'm sorry but I'm not able to understand what you are trying to achieve, can you provide some more info?
Values interpolation should work exactly like in i18n-js.

Sure. basically I have some text needed for translation "By using Application you accept the privacy politics and our terms of use." where privacy politics is the link or text with different styles.

I want to avoid splitting the text into multiple parts:

  • By using Application you accept the
  • privacy politics
  • and
  • terms of use

I want to translate the whole phrase like this
'login_condition': 'By using %{appName} you accept the %{privacyLink} and our %{termLink}',

and can't find the way to do it.

Correct me if I am mistaken but you're trying to interpolate React Component (not simple object/text) because you need to style a portion of text, am I right?
Unfortunately this is out of the scope of this library.
Basically you can interpolate text in this way:

// Translation definition:
WELCOME_MSG: 'Hello {{name}}!'

// In you component:
I18n.t('WELCOME_MSG', { name: 'Alexander' })

But you can't style Alexander differently from the rest of the text, you must create your own function/wrapper to do so... 馃檷

Closing, let me know if you find any cool solution to your issue!

@mmazzarolo can you guide in writing the wrapper

i am facing same issue and just make a quick solution:
create a function that return array of react components

const tWithComponent = (i18nKey, components, t) => {
      let string = t(i18nKey)
      let results = []
      components.forEach((component, idx) => {
        let splitedStrings = string.split('{' + idx + '}')
        results.push(splitedStrings[0])
        results.push(component)
        string = splitedStrings[1]
      })
      results.push(string)
      return results
    }
return (
...
{tWithComponent('doNotHaveAccountYet', [<Link key='0' to='/signup'>{t('signUp')}</Link>], t)}
...
)

i18n string will look like:

"doNotHaveAccountYet": "Do not have account yet? Click {0} to start!"

@TONYHOKAN - what is t?

@compojoom
i am using react-i18next and t is the function passed by I18n component to child function and triggers loading the translation files needed. I think the t is equal to I18n.t().

@TONYHOKAN thanks! Why are you passing t as parameter to the function?
I rewrote it to:

export const tWithComponent = (i18nKey, components) => {
  let string = I18n.t(i18nKey)
  let results = []
  components.forEach((component, idx) => {
    let splitedStrings = string.split('{' + idx + '}')
    results.push(splitedStrings[0])
    results.push(component)
    string = splitedStrings[1]
  })
  results.push(string)
  return results
}

and it works! Thanks again :)

@compojoom as i was using react-i18next HOC to wrap the parent component of tWithComponent and could not directly access t via global context. Your are right that we do not no need to pass t as parameter if you can access t function in global context.

I came up with a helper component that does the 'rich text interpolation'. It's written in ES5, and without semi-colon (airbnb rules).

import React from 'react'
import PropTypes from 'prop-types'
import { Text } from 'react-native'
import I18n from 'react-native-i18n'

export const RichI18n = (props) => {
  const { id, values, baseComponent, baseComponentStyle, baseComponentProps, ...other } = props
  // get the translations interpoling the placeholder by itself
  const keys = Object.keys(values)
  const placeHolders = {}
  keys.forEach((key) => placeHolders[key] = `{{${key}}}`)
  const translation = I18n.t(id, { ...(other || {}), ...placeHolders }) || ''
  // split the translated string, using default i18n-js regex, odd indexes will correlate to a key to be interpolated
  // https://github.com/fnando/i18n-js/blob/master/app/assets/javascripts/i18n.js
  const splitted = translation.split(/(?:\{\{|%\{)(.*?)(?:\}\}?)/gm)
  const interpolated = []
  splitted.forEach((str, idx) => {
    if ((idx % 2) === 0) {
      interpolated.push(str)
    } else {
      const child = values[str]
      if (typeof child === 'string') {
        interpolated.push(child)
      } else {
        interpolated.push(React.cloneElement(child, { key: idx }))
      }
    }
  })
  const BaseComponent = baseComponent
  return (
    <BaseComponent style={baseComponentStyle} {...baseComponentProps}>{interpolated}</BaseComponent>
  )
}

RichI18n.propTypes = {
  id: PropTypes.string.isRequired,
  values: PropTypes.object,
  baseComponent: PropTypes.any,
  baseComponentStyle: PropTypes.object,
  baseComponentProps: PropTypes.object
}

RichI18n.defaultProps = {
  values: {},
  baseComponent: Text,
  baseComponentStyle: {},
  baseComponentProps: {}
}

Usage:

 <RichI18n
  id="my.interpolation.test"
  defaultValue="This is an {{interpolation}} test {{plain}} where {{you}} can repeat {{interpolation}} variables {{translated}}"
  values={{
    interpolation: <Text style={{ fontWeight: 'bold' }}>bold</Text>,
    you: <Text style={{ color: 'red' }}>me</Text>,
    plain: 'Just plain text',
    translated: I18n.t('Translated text', { defaultValue: 'Translated text' })
  }}
/>
Was this page helpful?
0 / 5 - 0 ratings

Related issues

mphasize picture mphasize  路  7Comments

wyzzy picture wyzzy  路  5Comments

ankita-p17 picture ankita-p17  路  6Comments

henrikra picture henrikra  路  4Comments

timgcarlson picture timgcarlson  路  5Comments