diff --git a/README.md b/README.md index 2a21b3a..a9fb67a 100644 --- a/README.md +++ b/README.md @@ -32,187 +32,74 @@ yarn add eslint-plugin-lingui --dev **Note:** If you installed ESLint globally (using the `-g` flag) then you must also install `eslint-plugin-lingui` globally. -## Usage +## Flat Config (`eslint.config.js`) -Add `lingui` to the plugins section of your `.eslintrc` configuration file. You can omit the `eslint-plugin-` prefix: +### Recommended Setup -```json -{ - "plugins": ["lingui"] -} -``` - -Then configure the rules you want to use under the rules section. - -```json -{ - "rules": { - "lingui/no-unlocalized-strings": 2, - "lingui/t-call-in-function": 2, - "lingui/no-single-variables-to-translate": 2, - "lingui/no-expression-in-message": 2, - "lingui/no-single-tag-to-translate": 2, - "lingui/no-trans-inside-trans": 2, - "lingui/text-restrictions": [ - 2, - { - "rules": [ - { - "patterns": ["''", "’", "“"], - "message": "Error message" - } - ] - } - ] - } -} -``` - -## Supported Rules - -## no-unlocalized-strings - -Check that code doesn't contain strings/templates/jsxText what should be wrapped into `` or `i18n` - -### Options - -#### ignore - -The `ignore` option specifies exceptions not to check for -literal strings that match one of regexp patterns. - -Examples of correct code for the `{ "ignore": ["rgba"] }` option: - -```jsx -/*eslint lingui/no-unlocalized-strings ["error", {"ignore": ["rgba"]}]*/ -const a =
-``` - -#### ignoreFunction - -The `ignoreFunction` option specifies exceptions not check for -function calls whose names match one of regexp patterns. - -Examples of correct code for the `{ "ignoreFunction": ["showIntercomMessage"] }` option: +To enable all of the recommended rules for our plugin, add the following config: ```js -/*eslint lingui/no-unlocalized-strings: ["error", { "ignoreFunction": ["showIntercomMessage"] }]*/ -const bar = showIntercomMessage('Please, write me') -``` - -#### ignoreAttribute - -The `ignoreAttribute` option specifies exceptions not to check for JSX attributes that match one of ignored attributes. - -Examples of correct code for the `{ "ignoreAttribute": ["style"] }` option: +import pluginLingui from 'eslint-plugin-lingui' -```jsx -/*eslint lingui/no-unlocalized-strings: ["error", { "ignoreAttribute": ["style"] }]*/ -const element =
+export default [ + ...pluginLingui.configs['flat/recommended'], + // Any other config... +] ``` -By default, the following attributes are ignored: `className`, `styleName`, `type`, `id`, `width`, `height` +### Custom setup -#### ignoreProperty +Alternatively, you can load the plugin and configure only the rules you want to use: -The `ignoreProperty` option specifies property names not to check. - -Examples of correct code for the `{ "ignoreProperty": ["text"] }` option: - -```jsx -/*eslint lingui/no-unlocalized-strings: ["error", { "ignoreProperty": ["text"] }]*/ -const test = { text: 'This is ignored' } +```js +import pluginLingui from 'eslint-plugin-lingui' + +export default [ + { + plugins: { + lingui: pluginLingui, + }, + rules: { + 'lingui/t-call-in-function': 'error', + }, + }, + // Any other config... +] ``` -By default, the following properties are ignored: `className`, `styleName`, `type`, `id`, `width`, `height` +## Legacy Config (`.eslintrc`) -## t-call-in-function +### Recommended setup -Check that `t` calls are inside `function`. They should not be at the module level otherwise they will not react to language switching. +To enable all of the recommended rules for our plugin, add `plugin:lingui/recommended` in extends: -```jsx -import { t } from '@lingui/macro' - -// nope ⛔️ -const msg = t`Hello world!` - -// ok ✅ -function getGreeting() { - return t`Hello world!` +```json +{ + "extends": ["plugin:lingui/recommended"] } ``` -Check the [Lingui Docs](https://lingui.dev/tutorials/react-patterns#translations-outside-react-components) for more info. - -## no-expression-in-message - -Check that `` t` ` `` doesn't contain member or function expressions like `` t`Hello ${user.name}` `` or `` t`Hello ${getName()}` `` - -Such expressions would be transformed to its index position such as `Hello {0}` which gives zero to little context for translator. - -Use a variable identifier instead. - -```jsx -// nope ⛔️ -t`Hello ${user.name}` // => 'Hello {0}' - -// ok ✅ -const userName = user.name -t`Hello ${userName}` // => 'Hello {userName}' -``` - -## no-trans-inside-trans - -Check that no `Trans` inside `Trans` components. - -```jsx -// nope ⛔️ -Hello World! - -// ok ✅ -Hello World! -``` - -## no-single-variables-to-translate - -Doesn't allow single variables without text to translate like `{variable}` or `` t`${variable}` `` - -Such expression would pollute message catalog with useless string which has nothing to translate. - -## text-restrictions - -Check that strings/templates/jsxText doesn't contain patterns from the rules. - -This rules enforces a consistency rules inside your messages. - -### Options +### Custom setup -### rules - -`rules` is array of rules when one rule has structure +Alternatively, add `lingui` to the plugins section, and configure the rules you want to use: ```json { - "patterns": ["first", "second"], - "message": "error message" + "plugins": ["lingui"], + "rules": { + "lingui/t-call-in-function": "error" + } } ``` -each `rule` has a structure: - -- `patterns` is an array of regex or strings -- `message` is an error message that will be displayed if restricting pattern matches text -- `flags` is a string with regex flags for patterns -- `isOnlyForTranslation` is a boolean indicating that patterns should be found only inside `Trans` tags or `t` tagged template +## Rules -## no-single-tag-to-translate +✅ - Recommended -Ensures `` isn't wrapping a single element unnecessarily - -```jsx -// nope ⛔️ -Foo bar - -// ok ✅ -Foo bar -``` +- ✅ [no-expression-in-message](docs/rules/no-expression-in-message.md) +- ✅ [no-single-tag-to-translate](docs/rules/no-single-tag-to-translate.md) +- ✅ [no-single-variables-to-translate](docs/rules/no-single-variables-to-translate.md) +- ✅ [no-trans-inside-trans](docs/rules/no-trans-inside-trans.md) +- ✅ [t-call-in-function](docs/rules/t-call-in-function.md) +- [no-unlocalized-strings](docs/rules/no-unlocalized-strings.md) +- [text-restrictions](docs/rules/text-restrictions.md) diff --git a/docs/rules/no-expression-in-message.md b/docs/rules/no-expression-in-message.md new file mode 100644 index 0000000..c73d96e --- /dev/null +++ b/docs/rules/no-expression-in-message.md @@ -0,0 +1,16 @@ +# no-expression-in-message + +Check that `` t` ` `` doesn't contain member or function expressions like `` t`Hello ${user.name}` `` or `` t`Hello ${getName()}` `` + +Such expressions would be transformed to its index position such as `Hello {0}` which gives zero to little context for translator. + +Use a variable identifier instead. + +```jsx +// nope ⛔️ +t`Hello ${user.name}` // => 'Hello {0}' + +// ok ✅ +const userName = user.name +t`Hello ${userName}` // => 'Hello {userName}' +``` diff --git a/docs/rules/no-single-tag-to-translate.md b/docs/rules/no-single-tag-to-translate.md new file mode 100644 index 0000000..136ae0e --- /dev/null +++ b/docs/rules/no-single-tag-to-translate.md @@ -0,0 +1,14 @@ +# no-single-tag-to-translate + +> [!TIP] +> This rule is included into the `lingui/recommended` config + +Ensures `` isn't wrapping a single element unnecessarily + +```jsx +// nope ⛔️ +Foo bar + +// ok ✅ +Foo bar +``` diff --git a/docs/rules/no-single-variables-to-translate.md b/docs/rules/no-single-variables-to-translate.md new file mode 100644 index 0000000..74403ef --- /dev/null +++ b/docs/rules/no-single-variables-to-translate.md @@ -0,0 +1,16 @@ +# no-single-variables-to-translate + +> [!TIP] +> This rule is included into the `lingui/recommended` config + +Doesn't allow single variables without text to translate like `{variable}` or `` t`${variable}` `` + +Such expression would pollute message catalog with useless string which has nothing to translate. + +```jsx +// nope ⛔️ +{user} + +// ok ✅ +Hello {user} +``` diff --git a/docs/rules/no-trans-inside-trans.md b/docs/rules/no-trans-inside-trans.md new file mode 100644 index 0000000..464a416 --- /dev/null +++ b/docs/rules/no-trans-inside-trans.md @@ -0,0 +1,14 @@ +# no-trans-inside-trans + +> [!TIP] +> This rule is included into the `lingui/recommended` config + +Check that no `Trans` inside `Trans` components. + +```jsx +// nope ⛔️ +Hello World! + +// ok ✅ +Hello World! +``` diff --git a/docs/rules/no-unlocalized-strings.md b/docs/rules/no-unlocalized-strings.md new file mode 100644 index 0000000..ce831d5 --- /dev/null +++ b/docs/rules/no-unlocalized-strings.md @@ -0,0 +1,62 @@ +# no-unlocalized-strings + +Check that code doesn't contain strings/templates/jsxText what should be wrapped into `` or `i18n` + +> [!IMPORTANT] +> This rule might use type information. You can enable it with `{useTsTypes: true}` + +## Options + +### useTsTypes + +Use additional Typescript type information. Requires [typed linting](https://typescript-eslint.io/getting-started/typed-linting/) to be setup. + +### ignore + +The `ignore` option specifies exceptions not to check for +literal strings that match one of regexp patterns. + +Examples of correct code for the `{ "ignore": ["rgba"] }` option: + +```jsx +/*eslint lingui/no-unlocalized-strings ["error", {"ignore": ["rgba"]}]*/ +const a =
+``` + +### ignoreFunction + +The `ignoreFunction` option specifies exceptions not check for +function calls whose names match one of regexp patterns. + +Examples of correct code for the `{ "ignoreFunction": ["showIntercomMessage"] }` option: + +```js +/*eslint lingui/no-unlocalized-strings: ["error", { "ignoreFunction": ["showIntercomMessage"] }]*/ +const bar = showIntercomMessage('Please, write me') +``` + +### ignoreAttribute + +The `ignoreAttribute` option specifies exceptions not to check for JSX attributes that match one of ignored attributes. + +Examples of correct code for the `{ "ignoreAttribute": ["style"] }` option: + +```jsx +/*eslint lingui/no-unlocalized-strings: ["error", { "ignoreAttribute": ["style"] }]*/ +const element =
+``` + +By default, the following attributes are ignored: `className`, `styleName`, `type`, `id`, `width`, `height` + +### ignoreProperty + +The `ignoreProperty` option specifies property names not to check. + +Examples of correct code for the `{ "ignoreProperty": ["text"] }` option: + +```jsx +/*eslint lingui/no-unlocalized-strings: ["error", { "ignoreProperty": ["text"] }]*/ +const test = { text: 'This is ignored' } +``` + +By default, the following properties are ignored: `className`, `styleName`, `type`, `id`, `width`, `height` diff --git a/docs/rules/t-call-in-function.md b/docs/rules/t-call-in-function.md new file mode 100644 index 0000000..ef614d4 --- /dev/null +++ b/docs/rules/t-call-in-function.md @@ -0,0 +1,20 @@ +# t-call-in-function + +> [!TIP] +> This rule is included into the `lingui/recommended` config + +Check that `t` calls are inside `function`. They should not be at the module level otherwise they will not react to language switching. + +```jsx +import { t } from '@lingui/macro' + +// nope ⛔️ +const msg = t`Hello world!` + +// ok ✅ +function getGreeting() { + return t`Hello world!` +} +``` + +Check the [Lingui Docs](https://lingui.dev/tutorials/react-patterns#translations-outside-react-components) for more info. diff --git a/docs/rules/text-restrictions.md b/docs/rules/text-restrictions.md new file mode 100644 index 0000000..a461d1a --- /dev/null +++ b/docs/rules/text-restrictions.md @@ -0,0 +1,23 @@ +# text-restrictions + +Check that strings/templates/jsxText doesn't contain patterns from the rules. + +This rules enforces a consistency rules inside your messages. + +## rules + +`rules` is array of rules when one rule has structure + +```json +{ + "patterns": ["first", "second"], + "message": "error message" +} +``` + +each `rule` has a structure: + +- `patterns` is an array of regex or strings +- `message` is an error message that will be displayed if restricting pattern matches text +- `flags` is a string with regex flags for patterns +- `isOnlyForTranslation` is a boolean indicating that patterns should be found only inside `Trans` tags or `t` tagged template diff --git a/src/create-rule.ts b/src/create-rule.ts index 3dc66c4..1364f17 100644 --- a/src/create-rule.ts +++ b/src/create-rule.ts @@ -5,5 +5,5 @@ export type ExtraRuleDocs = { } export const createRule = ESLintUtils.RuleCreator( - (name) => `https://github.com/lingui/eslint-plugin?tab=readme-ov-file#${name}`, + (name) => `https://github.com/lingui/eslint-plugin/blob/main/docs/rules/${name}.md`, )