@ices/react-locale
React locale components.
Install
npm install @ices/react-locale
npm install -D @ices/locale-webpack-plugin
Usage
// webpack.config.js
const LocalePlugin = require('@ices/locale-webpack-plugin')
module.exports = {
plugins: [new LocalePlugin()],
}
// foo.tsx
import React, { useCallback } from 'react'
import useTrans from './lang.yml'
function ToggleLocaleButton() {
const initialLocale = 'zh-CN' // or () => 'zh-CN'
const fallback = 'zh'
const [trans, locale, setLocale] = useTrans([], initialLocale, fallback)
const toggleLocale = useCallback(() => {
setLocale(locale === 'en-US' ? 'zh-CN' : 'en-US')
}, [locale, setLocale])
return <button onClick={toggleLocale}>{trans('ToggleLocale')}</button>
}
// boo.tsx
import React from 'react'
import { setLocale, setFallbackLocale, getLocale } from '@ices/react-locale'
import { Trans } from './lang.yml'
setLocale('zh-CN')
setFallbackLocale('zh')
class ToggleLocaleButton extends React.Component<any, any> {
//
toggleLocale() {
setLocale(getLocale() === 'en-US' ? 'zh-CN' : 'en-US')
}
render() {
return (
<button onClick={this.toggleLocale}>
<Trans id="ClickMe" />
</button>
)
}
}
# lang.yml
#include zh-CN
#include en-US
zh-CN:
ToggleLocale: 切换语言
en-US:
ToggleLocale: Change Locale
# zh-CN.yml
#include "../some/relative/path/foo.yml"
ClickMe: 快点我!
# en-US.yml
#include <some-module-from-node-modules/boo.yml>
ClickMe: Click Me!
Others
// foo.tsx
import React from 'react'
import {
setLocale,
getLocale,
determineLocale,
LocaleContext,
LocaleProvider,
} from '@ices/react-locale'
// Import a language module bound to the current module
import { Trans, useTrans, useContextTrans } from './lang.yml'
import { somePlugin } from './plugins.ts'
function MyButton() {
const plugins = [somePlugin]
const fallback = 'zh'
// Hooks that not bind a locale context will use the global locale state
// You can use some plugins (optional) to format localized message
// Default plugin will process variable placeholder for "{ var }"
// Fallback (optional) is the alternate language in this bound trans
const [trans] = useTrans(plugins, fallback)
return <button>{trans('message-key', { foo: true }, 'pluginArgTwo')}</button>
}
const initialLocaleLang = determineLocale({
urlLocaleKey: 'lang',
cookieLocaleKey: 'lang',
storageLocaleKey: 'lang',
fallbackLocale: 'zh-CN',
})
function ContextButton() {
const plugins = []
const fallback = 'zh'
// You can bind the locale state to some context
// Hooks that bound to some locale context will independent of others hook
// plugins and fallback are optional args
const [trans] = useContextTrans(LocaleContext, plugins, fallback)
return <button>{trans('message-key')}</button>
}
// Bind the context to the Trans Component
// From v2.3.0, TransComponent will bound the LocaleContext by default.
Trans.contextType = LocaleContext
// Global set this componet to support output html content.
Trans.enableDangerouslySetInnerHTML = true
class ContextButtonComponent extends React.Component<any, any> {
render() {
return (
<LocaleProvider value={initialLocaleLang}>
<button>
{/* enableHTML prop is prior to the static prop of enableDangerouslySetInnerHTML */}
<Trans enableHTML={false} id="message-key" plugins={somePlugin} data={{ foo: true }} />
</button>
{/* The Function component ContextButton will bind the locale to the LocaleContext */}
<ContextButton />
</LocaleProvider>
)
}
}
// plugins.ts
// You can import a language module for plug-ins
import { definitions } from './plugin-lang.yml'
// some plugin
export function somePlugin(message, [{ foo }, pluginArgTwo], translate) {
// Plugins can also use the translate function
return `${message}-processed by ${translate('message-key', definitions)}`
}
// boo.ts
import {
withDefinitionsHook,
withDefinitionsContextHook,
withDefinitionsComponent,
} from '@ices/react-locale'
import localeData from 'other-locale-data-module'
// - {[lang:string]: {[key:string]: string }}
// - (lang:string)=>Promise<{[lang:string]: {[key:string]: string}}>
// You can use hook or component with customize locale data
const useTransHook = withDefinitionsHook(localeData)
const useContextTransHook = withDefinitionsContextHook(localeData)
const TranslateComponent = withDefinitionsComponent(localeData)
// foo.ts
import { useTraslator } from '@ices/react-locale'
import localeData from 'other-locale-data-module'
function MyComponent() {
// The localeData var can be:
// - {[lang:string]: {[key:string]: string }} // just like: {en: {key: 'some'}}
// - {[lang:string]: ()=>Promise<{default: {[key:string]: string }}}>} // just like: {en: ()=>import('./xx/locales/en.json')}
// - (lang:string)=>Promise<{[lang:string]: {[key:string]: string}}> // legency support
const { useTrans, useContextTrans, Translate } = useTraslator(localeData)
}
#include
# lang.yml
# You can use "#include" annotation to "import" other locale module to the current module
# -----------------------------------------------------------------------------------------
# Using quotes( ' or " ) or non-quotes will include files relative to the current module
# This will merge "./zh-CN.yml" or "./zh-CN.yaml" module to the current module:
# -----------------------------------------------------------------------------------------
#include zh-CN
#include ./zh-CN.yml
#include "zh-CN"
#include "./zh-CN"
#include "./zh-CN.yml"
#include 'zh-CN'
#include './zh-CN'
#include './zh-CN.yml'
# -----------------------------------------------------------------------------------------
# This will merge "./dir/index.yml" or "./dir/index.yaml" module to the current module:
# -----------------------------------------------------------------------------------------
#include dir
#include "dir"
#include 'dir'
#include "./dir"
#include './dir'
# -----------------------------------------------------------------------------------------
# Using angle bracket( < > ) will include files from node_modules
# This will merge "node_modules/foo/boo.yml" or "node_modules/foo/boo.yaml" module to the current module:
# -----------------------------------------------------------------------------------------
#include <foo/boo>
#include <foo/boo.yml>
#include <foo/boo.yaml>
# -----------------------------------------------------------------------------------------
Notice
import { Trans as ZhTrans, useTrans as useZhTrans } from './zh.yml'
import { Trans as EnTrans, useTrans as useEnTrans } from './en.yml'
console.log(ZhTrans !== EnTrans) // true
console.log(useZhTrans !== useEnTrans) // true
const [enTrans] = useEnTrans()
enTrans('message-key-from-zh.yml') // throw not found message error
function Button() {
return (
<button>
{/* throw not found message error */}
<EnTrans id="message-key-from-zh.yml" />
</button>
)
}
// So, the imported language module is independent of the other language modules
Data Merge
# ----------------------------
# lang.yml
# ----------------------------
zh:
foo: 一些东西
en:
foo: Something
# ----------------------------
# This will be exported like:
# ----------------------------
# {
# zh: {
# foo: '一些东西'
# },
# en: {
# foo: 'Something'
# }
# }
# ----------------------------
# zh.yml
# ----------------------------
foo: 一些东西
boo: 什么东西
# ----------------------------
# en-US.yml
# ----------------------------
foo: Some thing
boo: Things
# ----------------------------
# lang.yml
# ----------------------------
#include zh
#include en-US
zh:
boo: 另一些东西
en:
boo: Another thing
# ----------------------------
# This will be exported like:
# ----------------------------
# {
# zh: {
# foo: '一些东西',
# boo: '另一些东西'
# },
# en-US: {
# foo: 'Some thing',
# boo: 'Things'
# },
# en: {
# boo: 'Another thing'
# }
# }
Suspend Console Warning
When a fallback language is used, a warning message is printed on the console, which you can disable it like this:
window.__suspendReactLocaleWarning = true