Auto-translate for your application (TSX/React supported)
npm i --save-dev tx-i18n
- ICU Message Format
- No additional markup (Just compare with react-intl or react-i18next)
- Fastest (see benchmarks)
- Context
- Pluralization by CLDR (todo)
- Support
- Litteral strings
- Template strings
- TSX/React: Text, Expression, Attributes, Fragment & Tags (any complexity)
- Not supproted
- Object keys
- TSX/React and Storybook Addons
ttypescript β it's not a typo ππ»
npm i --save-dev ttypescript
{
"compilerOptions": {
"plugins": [{
"transform": "tx-i18n/plugin",
"humanTextCheckRegExp": "[Π°-ΡΡ]",
"output": "./src/locale/default.ts"
}]
},
}
{
"name": "your-package",
"scripts": {
"build": "ttsc"
}
}
const { i18nTx, i18nExtractor } = require('tx-i18n/webpack');
module.exports = {
// ...
module: {
rules: [{
test: /\.tsx?$/,
loader: 'awesome-typescript-loader',
exclude: /node_modules/,
options: {
getCustomTransformers: () => ({
before: [
i18nTx({}), // <!--- (1) TypeScript i18n Transformer
],
after: [],
}),
},
}],
},
plugins: [
new i18nExtractor({
output: './src/locale/__default__.ts', // <!--- (2) Extract original phrases as ES Module
}),
],
};
import { setLang, setLocale } from 'tx-i18n';
import { plural } from 'tx-i18n/icu/plural/en';
import locale from './locale/en';
setLocale('en', locale, plural);
setLang('en');
export default {
'default': {
'Hi, <#1>!': 'Hi, <#1>!',
'Click <1>here</1> for help': 'Click <1>here</1> for help',
},
};
const dict = {
firstName: 'ΠΠΌΡ',
lastName: 'Π€Π°ΠΌΠΈΠ»ΠΈΡ',
};
/** @tx-i18n context: personal */
const personalDict = {
firstName: 'ΠΠΌΡ',
lastName: 'Π€Π°ΠΌΠΈΠ»ΠΈΡ',
};
const Dialog = () => (
<div>
{/** @tx-i18n context: personal */}
<form>
<h1>ΠΠ°ΡΠΈ Π΄Π°Π½Π½ΡΠ΅</h1>
<fieldset>...</fieldset>
</form>
</div>
);
export default {
'default': {
'ΠΠΌΡ': 'ΠΠΌΡ',
'Π€Π°ΠΌΠΈΠ»ΠΈΡ': 'Π€Π°ΠΌΠΈΠ»ΠΈΡ',
},
'personal': {
'ΠΠΌΡ': 'ΠΠΌΡ',
'Π€Π°ΠΌΠΈΠ»ΠΈΡ': 'Π€Π°ΠΌΠΈΠ»ΠΈΡ',
'ΠΠ°ΡΠΈ Π΄Π°Π½Π½ΡΠ΅': 'ΠΠ°ΡΠΈ Π΄Π°Π½Π½ΡΠ΅',
},
};
1. Create a file called addons.js
in your Storybook config, if there is no any and append following line:
import 'tx-i18n/storybook-addon/register';
import { addParameters, addDecorator } from '@storybook/react';
import { withTXI18n } from 'tx-i18n/storybook-addon';
import en from '../locale/en'; // any locale of your application
addParameters({
'tx-i18n': {
locales: {en},
defaultLang: 'ru',
},
});
addDecorator(withTXI18n);
import { enPlural as plural } from 'tx-i18n/plural/en';
const Hello = ({name, unreadCount}) => (
<div>
Hello <b>{name}</b>!<br/>
You have <a href="#unread">{plural(unreadCount, {one: '# message', other: '# messages'})}</a>.
</div>
);
Get a current lang.
Change a current lang.
- lang:
string
Set a locale for a lang.
- lang:
string
- locale:
Locale
Add an observer on a lang changes.
- listener:
(lang, prevLang) => void
Get a translate for a phrase.
- phrase:
string
- lang:
string
Is a magic typescript transformer ;]
- fnName:
string
β the name of the function that will be used to wrap strings. (optional, default:__
) - packageName:
string
β the name of the package from which will be exported as default the function with the namefnName
. (optional, default:tx-i18n
) - include:
Array<string|regexp>
β an array of files or paths that need for a transform. (optional) - exclude:
Array<string|regexp>
β an array of files or paths that need to exclude for a transform. (optional) - pharsesStore:
ContextedPharses
β a reference to a variable which will be used to collect phrases (optional) - normalizeText:
(text: string) => string
β (optional) - isHummanText:
(text: string, node: ts.Node) => boolean
β (optional) - isTranslatableJsxAttribute:
(attr: ts.JsxAttribute, elem: ts.JsxElement) => boolean
β (optional) - overrideHumanTextChecker:
(isHummanText: HumanTextChecker) => HumanTextChecker
β (optional)
Is a webpack plugin for save all phrases to translate
- output:
string | (phases: ContextedPhrases) => Array<{file: string; phases: ContextedPhrases}>
β the filename or function that returns the array for separationphrases
by files..json
β save asjson
- If you use
.ts
or.js
, file will be saved as ES Module.
- stringify:
(locale: ContextedLocale) => string
β convertor to json before save (optional) - indent:
string
β
type ContextedPhrases = {
[context: string]: Pharse[];
}
type Pharse = {
value: string;
file: string;
loc: {
start: {
line: number;
character: number;
};
end: {
line: number;
character: number;
};
};
}
type ContextedLocale = {
[context: string]: Locale;
}
type Locale = {
[pharse: string]: string;
}
Using the Compiler API, i18Tx
traversing the AST-tree and wrap the text nodes a special function + add the import of this function, it looks like this:
// Original
const text = 'Hello world';
// Transformed (after bundle build)
import __ from 'tx-i18n';
const text = __('Hello world');
// Original
const text = `Hi, ${username}!`;
// Transformed (after bundle build)
import __ from 'tx-i18n';
const text = __('Hi, {v1}!', [username]);
// Original
const Fragment = () => (
<div title="This is fragment" data-name="frag">
<h1>Fragment of HTML</h1>
<div>
Click <a href="#help" title="How to use tx-i18n">here</a> for detail.
</div>
</div>
);
// Transformed (after bundle build)
import __ from 'tx-i18n';
const Fragment = () => (
<div title={__('This is fragment')} data-name="frag">
<h1>{__('Fragment of HTML')}</h1>
{__.jsx('Click <1>here</1> for detail.', [
{type: 'div', props: {}},
{
type: 'a',
props: {
href: '#help'
title: __('How to use tx-i18n'),
},
},
])}
</div>
);
npm i
npm test
, code coverage
- webpack: 4.29
- typescript: 3.0
- awesome-typescript-loader: 5.2