A deadly simple i18n translate plugin for React, ready for Server Side Rendering.
https://JounQin.github.io/react-translator
# npm
npm install react-translator2
# yarn
yarn add react-translator2import { TranslatorContext, createTranslator, withTranslator } from 'react-translator2'
const translator = createTranslator({
  locale?: string, // set it on initialize or before first rendering
  translations?: {  // If you want to define translations in component only, no need to set it on initialize
    [locale: string]: {
      [key:string]: string | array | object
    }
  },
  defaultLocale?: string, // when no value can be found in current locale, try to fallback to defaultLocale
  merge?: Function // `lodash.merge` for example, if you want to use component translator you must pass it
})
const App = withTranslator(translations?)(({ t }) => <div>{t('msg')}</div>)
const app = (
  <TranslatorContext.Provider value={{ translator, toggleLocale }}>
    <App />
  </TranslatorContext.Provider>
)translations is often generated via require.context provided by webpack from *.{locale}.i18n.json:
const context = require.context('.', true, /([\w-]*[\w]+)\.i18n\.json$/)
const LOCALE_KEYS: { [key: string]: string[] } = {}
const translations: {
  [locale: string]: {
    [key: string]: string
  }
} = context.keys().reduce((modules: any, key: string) => {
  const module = context(key)
  const lang = key.match(/([\w-]*[\w]+)\.i18n\.json$/)[1]
  const matched = modules[lang] || (modules[lang] = {})
  if (process.env.NODE_ENV === 'development') {
    const keys = LOCALE_KEYS[lang] || (LOCALE_KEYS[lang] || [])
    const moduleKeys = Object.keys(module)
    const duplicates = _.intersection(keys, moduleKeys)
    if (duplicates.length) {
      console.warn('detect duplicate keys:', duplicates)
    }
    keys.push(...moduleKeys)
  }
  Object.assign(matched, module)
  return modules
}, {})Then you need to use withTranslator to wrap your component to enable translator prop t and prop locale + defaultLocale, the reference value of t will never change what means there will be only one translator instance..
import { withTranslator } from 'react-translator2'
export default withTranslator({
  zh: {
    message: '我的信息',
  },
  en: {
    message: 'My Message',
  },
})(({ t }) => (
  <div>
    {t('message', obj_params?)}
    {t('nested.message', arr_params?)}
  </div>
))If you are trying to get a non-exist key or value is undefined, you will get a warning in console on development. And if you want to ignore it, pass a third parameter ignoreNonExist: boolean: $t('non-exist-key', null, true).
If you want to watch locale change in any component, you can use componentDidUpdate lifecycle:
componentDidUpdate(prevProps: TranslatorProps) {
  if(prevProps.locale !== this.props.locale) {
    // locale changed
  }
}If you want to change locale on client:
{
  changeLocale() {
    this.props.toggleLocale(locale)
    this.props.toggleDefaultLocale(defaultLocale)
  }
}You'd better to detect user custom locale via cookie and fallback to accept-language on first request.
And you need to generate a single translator instance for every user request (cache by locale would be better) via createTranslator, koa for example:
import { TranslatorContext, createTranslator } from 'react-translator2'
app.use(async (ctx, next) => {
  const translator = createTranslator({
    locale: string, // ctx.cookies.get('locale_cookie')
    defaultLocale: string,
  })
  const app = (
    <TranslatorContext.Provider value={{ translator, toggleLocale }}>
      <App />
    </TranslatorContext.Provider>
  )
})Translation key should be string, but .(dot) will be parsed as nested key, it will also work in template!
t('a.b.c') // will try to find `a.b.c` on your custom transition, if a is falsy, will render undefined and try default locale
// render `nested value`
withTranslator({
  en: {
    a: {
      b: {
        c: 'nested value',
      },
    },
  },
})
// render `nested template`
t('a.b', {c: d: 'nested template'})
withTranslator({
  en: {
    a: {
      b: '{ c.d }'
    },
  },
})Array is also supported, .index(dot) or [index] can both be used!
// nested with array key and template
// render `1`
t('a.b[0]', [{ a: 1 }])
withTranslator({
  en: {
    a: {
      b: ['{ 0.a }'], // do not use `[0].a` here, `0[a]` is also OK
    },
  },
})Feel free to create an issue.