@ts-intl/eslint-plugin-ts-intl
v2.0.0
Published
ESLint plugin for js/ts/jsx/tsx
Downloads
224
Maintainers
Readme
eslint-plugin-ts-intl
ESLint plugin for js/ts/jsx/tsx
, improving stability and efficiency of I18N project.
best fit next-intl, also work well with other i18n plugins like i18next and custom translation methods etc.
Installation
npm install --save-dev @ts-intl/eslint-plugin-ts-intl
Rules
@ts-intl/ts-intl/no-dynamic-keys
Disallow localization dynamic keys at localization methods.
Why: Easier to track.
const rule = {
'@ts-intl/ts-intl/no-dynamic-keys': [
'error',
{
funcNamePattern: '^(t|\\$t)$', // t('keyA')
hookNamePattern: '^useTranslations$', // useTranslations()('keyA')
richNamePattern: '^rich$', // t.rich('keyA')
},
],
};
useTranslations()('keyA'); // good
useTranslations()(keyA); // error
@ts-intl/ts-intl/no-mismatch-t
Disallow mismatch translation method name.
Why: Easier to track.
const rule = {
'@ts-intl/ts-intl/no-mismatch-t': [
'error',
{
funcNamePattern: '^(t|\\$t)$',
hookNamePattern: '^useTranslations$',
},
],
};
const t = useTranslations(); // good
const tt = useTranslations(); // error
@ts-intl/ts-intl/no-nested-call
Disallow nested translation method calls.
Why: There are differences of translation between languages, the best practice is treating phrase as a whole.
const rule = {
'@ts-intl/ts-intl/no-nested-call': [
'error',
{
...intlCommonConfig,
},
],
};
const t = useTranslation();
t('a {animal}', {
animal: t('dog'), // error
});
t('a dog'); // good
t('a cat'); // good
@ts-intl/ts-intl/no-namespace-hooks
Disallow localization hook(or translation method factory) call with namespace.
Why: pure translation method calling is easier to track and do some automation.
const rule = {
'@ts-intl/ts-intl/no-namespace-hooks': [
'error',
{
hookNamePattern: '^useTranslations|tFactory$',
},
],
};
useTranslation()('global.keyA'); // good
useTranslation('global')('keyA'); // error
@ts-intl/ts-intl/no-forbidden-keys
Disallow localization forbidden keys.
Why: Avoid using reserved words.
const rule = {
'@ts-intl/ts-intl/no-forbidden-keys': [
'error',
{
funcNamePattern: '^(t|\\$t)$',
hookNamePattern: '^useTranslations$',
richNamePattern: '^rich$',
forbiddenPattern: ':',
},
],
};
useTranslation()('global.keyA'); // good
useTranslation()('global.key:A'); // error
@ts-intl/ts-intl/no-invalid-keys
Disallow localization missing/invalid keys.
const rule = {
'@ts-intl/ts-intl/no-invalid-keys': [
'error',
{
funcNamePattern: '^(t|\\$t)$',
hookNamePattern: '^useTranslations$',
richNamePattern: '^rich$',
nsDivider: '.',
keyDivider: '.',
localePath: '/src/locales',
locale: 'en',
fallbackNamespace: 'common',
},
],
};
// locale directory structure:
// /src/locales/en/common
// /src/locales/en/global
// common: { keyA: 'keyA', keyC: { keyD: 'keyD' } }
t('keyA'); // error: missing namespace, auto prepend a namespace if keyA exist in it, otherwise prepend fallbackNamespace
t('common.keyB'); // error: key do not exist in locale
t('other.keyA'); // error: namespace do not exist in locale
t('common.keyA'); // error: value of key is empty string
@ts-intl/ts-intl/no-raw-text
Detect potential natural language which need to be translated.
const rule = {
'@ts-intl/ts-intl/no-raw-text': [
'warn',
{
funcNamePattern: '^(t|\\$t)$', // ignore the phrases in t call
hookNamePattern: '^useTranslations$', // ignore the phrases in t factory call
richNamePattern: '^rich$', // // ignore the phrases in t.rich call
include: {
body: '\\/ui\\/src', // path pattern, which files should be detected
flags: 'i', // RegExp flags
},
exclude: [
{
body: '\\.test\\.tsx?', // path pattern, ignore file paths
},
],
matchedPattern: '[a-zA-Z]{2,}', // test phrases which need to be translated
ignoreNodes: [
{
type: 'JSXAttribute',
body: getPatternByArr(matchedJSXAttrs, true), // normally we ignore most JSXAttribute except title/desc/name etc.
flags: 'i',
},
{
type: 'Property',
body: getPatternByArr(ignoreCustomProperties),
flags: 'i',
},
{
type: 'Property',
body: getPatternByArr(ignoreCSSProperties),
},
{
type: 'Property,VariableDeclarator,AssignmentPattern',
body: getPatternByArr(ignoreIndexNames),
flags: 'i',
},
{
type: 'CallExpression',
body: getPatternByArr(ignoreCallNames),
},
],
ignoreTexts: [
{
body: getPatternByArr(ignoreCSSTexts),
},
{
body: getPatternByArr(ignoreReservedWords),
},
{
body: getPatternByArr(ignoreUrlPatterns),
},
],
},
],
};
@ts-intl/ts-intl/syntax-icu-ts
Disallow not icu style keys. Open this rule when using ICU Message syntax.
const rule = {
'@ts-intl/ts-intl/syntax-icu-ts': [
'error',
{
funcNamePattern: '^(t|\\$t)$',
hookNamePattern: '^useTranslations$',
richNamePattern: '^rich$',
nsDivider: '.',
keyDivider: '.',
localePath: '/src/locales',
locale: 'en',
},
],
};
t('common.a {animal}', { animal: 'dog' }); // good
t('common.a {animal}'); // error, missing animal parameter
t.rich('common.a <blue>bird</blue>'); // error, missing blue renderer
t('common.a <blue>bird</blue>', {
// error, should use t.rich
blue: (child) => child,
});
@ts-intl/ts-intl/no-missing-keys-in-other-locales
Disallow missing keys in other locales.
const eslintConfig = {
overrides: [
{
files: ['*.json'],
parser: 'jsonc-eslint-parser',
parserOptions: {
jsonSyntax: 'JSON',
},
rules: {
'@ts-intl/ts-intl/no-missing-keys-in-other-locales': [
'warn',
{
localePath: '/src/locales',
locale: 'en',
others: ['fr'],
keyDivider: '.',
nsDivider: '.',
},
],
},
},
],
};
// en
{
"a": "a",
"b": "b", // warning
}
// fr
{
"a": "a",
}
@ts-intl/ts-intl/syntax-icu-json
Disallow not icu style key-value in json. Open this rule when using ICU Message syntax.
const eslintConfig = {
overrides: [
{
files: ['*.json'],
parser: 'jsonc-eslint-parser',
parserOptions: {
jsonSyntax: 'JSON',
},
rules: {
'@ts-intl/ts-intl/syntax-icu-json': [
'error',
{
localePath: '/src/locales',
locale: 'en',
forbiddenPattern: '\\.',
},
],
},
},
],
};
{
"a.1": "a", // error
"a2": "a </>" // error
}