README
🈂️ tx-i18n
Auto-translate for your application (TSX/React supported)
npm i --save-dev tx-i18n
Feature
- 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
Usage with "pure" TypeScript
ttypescript — it's not a typo 👐🏻
npm i --save-dev ttypescript
tsconfig.json
in compilerOptions
section
Add transformer-plugin to {
"compilerOptions": {
"plugins": [{
"transform": "tx-i18n/plugin",
"humanTextCheckRegExp": "[а-яё]",
"output": "./src/locale/default.ts"
}]
},
}
package.json
and use ttsc
(yep, double t
) instead of tsc
Edit {
"name": "your-package",
"scripts": {
"build": "ttsc"
}
}
Usage with webpack
webpack.config.js
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
}),
],
};
app-entry-point.ts
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');
./src/locale/__default__.ts
export default {
'default': {
'Hi, <#1>!': 'Hi, <#1>!',
'Click <1>here</1> for help': 'Click <1>here</1> for help',
},
};
Context
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>
);
./src/locale/__default__.ts
export default {
'default': {
'Имя': 'Имя',
'Фамилия': 'Фамилия',
},
'personal': {
'Имя': 'Имя',
'Фамилия': 'Фамилия',
'Ваши данные': 'Ваши данные',
},
};
Storybook
addons.js
in your Storybook config, if there is no any and append following line:
1. Create a file called import 'tx-i18n/storybook-addon/register';
config.js
)
2. Then in your story's config or in a global config for the project (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);
Pluralization
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>
);
API
getLang(): string
Get a current lang.
setLang(lang)
Change a current lang.
- lang:
string
setLocale(lang, locale)
Set a locale for a lang.
- lang:
string
- locale:
Locale
addLangObserver(listener): unobserve
Add an observer on a lang changes.
- listener:
(lang, prevLang) => void
getTranslate(phrase[, lang]): string
Get a translate for a phrase.
- phrase:
string
- lang:
string
Internal API
i18nTx
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)
i18nExtractor
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;
}
How it works
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:
Simple text
// Original
const text = 'Hello world';
// Transformed (after bundle build)
import __ from 'tx-i18n';
const text = __('Hello world');
Literal template
// Original
const text = `Hi, ${username}!`;
// Transformed (after bundle build)
import __ from 'tx-i18n';
const text = __('Hi, {v1}!', [username]);
TSX / React
// 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>
);
Development
npm i
npm test
, code coverage
Support
v0.5
- webpack: 4.29
- typescript: 3.0
- awesome-typescript-loader: 5.2