1.0.1 • Published 2 years ago

tx-i18n v1.0.1

Weekly downloads
3
License
MIT
Repository
github
Last release
2 years ago

🈂️ tx-i18n

Auto-translate for your application (TSX/React supported)

npm i --save-dev tx-i18n

Feature


Usage with "pure" TypeScript

ttypescript — it's not a typo 👐🏻

npm i --save-dev ttypescript
Add transformer-plugin to tsconfig.json in compilerOptions section
{
  "compilerOptions": {
    "plugins": [{
      "transform": "tx-i18n/plugin",
      "humanTextCheckRegExp": "[а-яё]",
      "output": "./src/locale/default.ts"
    }]
  },
}
Edit package.json and use ttsc (yep, double t) instead of tsc
{
  "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

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';

2. Then in your story's config or in a global config for the project (config.js)

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 name fnName. (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 separation phrases by files.
    • .json — save as json
    • 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 (optional)
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


Support

v0.5

  • webpack: 4.29
  • typescript: 3.0
  • awesome-typescript-loader: 5.2
1.0.1

2 years ago

1.0.0-rc.5

2 years ago

1.0.0-rc.6

2 years ago

1.0.0-rc.3

2 years ago

1.0.0-rc.4

2 years ago

1.0.0-rc.1

2 years ago

1.0.0-rc.2

2 years ago

1.0.0-beta.3

3 years ago

1.0.0-beta.2

4 years ago

1.0.0-beta.1

4 years ago

0.6.0-rc.2

4 years ago

0.6.0-rc.3

4 years ago

0.6.0-rc.4

4 years ago

0.6.0-rc.5

4 years ago

0.6.0-rc.1

4 years ago

0.5.2

5 years ago

0.5.1

5 years ago

0.5.0

5 years ago

0.5.0-rc.1

5 years ago

0.4.3

5 years ago

0.4.2

6 years ago

0.4.1

6 years ago

0.4.0

6 years ago

0.3.7

6 years ago

0.3.6

6 years ago

0.3.5

6 years ago

0.3.4

6 years ago

0.3.3

6 years ago

0.3.2

6 years ago

0.3.1

6 years ago

0.3.0

6 years ago

0.2.1

6 years ago

0.2.0

6 years ago

0.1.1

6 years ago