From 28d73ff5ae5734055bc77ac4cc8b24007d93aacd Mon Sep 17 00:00:00 2001
From: edvardchen <>
Date: Thu, 20 Jun 2019 19:25:23 +0800
Subject: [PATCH 1/7] refactor: use rule selectors to reduce code complexity
BREAKING CHANGE: Disable fix because key in the call i18next.t(key) ussally was not same as the plain text
---
lib/rules/no-literal-string.js | 147 ++++++++++++++++-----------
package-lock.json | 70 +++++++++++++
package.json | 2 +
tests/lib/rules/no-literal-string.js | 28 +++++
4 files changed, 185 insertions(+), 62 deletions(-)
diff --git a/lib/rules/no-literal-string.js b/lib/rules/no-literal-string.js
index 8703e8e..2dcccf2 100644
--- a/lib/rules/no-literal-string.js
+++ b/lib/rules/no-literal-string.js
@@ -17,7 +17,6 @@ module.exports = {
category: 'Best Practices',
recommended: true
},
- fixable: 'code', // or "code" or "whitespace"
schema: [
{
type: 'object',
@@ -61,6 +60,7 @@ module.exports = {
function isValidFunctionCall({ callee }) {
let calleeName = callee.name;
+ if (callee.type === 'Import') return true;
if (callee.type === 'MemberExpression') {
if (calleeWhitelists.simple.indexOf(callee.property.name) !== -1)
@@ -74,61 +74,96 @@ module.exports = {
return calleeWhitelists.simple.indexOf(calleeName) !== -1;
}
+ const atts = ['className', 'style', 'styleName', 'src', 'type', 'id'];
+ function isValidAttrName(name) {
+ return atts.includes(name);
+ }
+
//----------------------------------------------------------------------
// Public
//----------------------------------------------------------------------
+ const visited = [];
+ function Literal(node) {}
+
+ function getNearestAncestor(node, type) {
+ let temp = node.parent;
+ while (temp) {
+ if (temp.type === type) {
+ return temp;
+ }
+ temp = temp.parent;
+ }
+ return temp;
+ }
+
const scriptVisitor = {
- Literal(node) {
+ 'JSXAttribute Literal'(node) {
+ const parent = getNearestAncestor(node, 'JSXAttribute');
+
+ // allow
+ if (isValidAttrName(parent.name.name)) {
+ visited.push(node);
+ }
+ },
+
+ 'ImportDeclaration Literal'(node) {
+ // allow (import abc form 'abc')
+ visited.push(node);
+ },
+
+ 'VariableDeclarator > Literal'(node) {
+ // allow statements like const A_B = "test"
+ if (isUpperCase(node.parent.id.name)) visited.push(node);
+ },
+
+ 'Property > Literal'(node) {
+ if (visited.includes(node)) return;
+ const { parent } = node;
+ // if node is key of property, skip
+ if (parent.key === node) visited.push(node);
+
+ // name if key is Identifier; value if key is Literal
+ // dont care whether if this is computed or not
+ if (isUpperCase(parent.key.name || parent.key.value))
+ visited.push(node);
+ },
+
+ 'CallExpression Literal'(node) {
+ if (visited.includes(node)) return;
+ const parent = getNearestAncestor(node, 'CallExpression');
+ if (isValidFunctionCall(parent)) visited.push(node);
+ },
+
+ 'JSXElement > Literal'(node) {
+ scriptVisitor.JSXText(node);
+ },
+
+ // @typescript-eslint/parser would parse string literal as JSXText node
+ JSXText(node) {
+ const trimed = node.value.trim();
+ visited.push(node);
+
+ if (!trimed || match(trimed)) {
+ return;
+ }
+
+ context.report({ node, message });
+ },
+
+ 'Literal:exit'(node) {
+ if (visited.includes(node)) return;
+
if (typeof node.value === 'string') {
const trimed = node.value.trim();
if (!trimed) return;
const { parent } = node;
- if (isUpperCase(trimed) && parent.type !== 'JSXElement') return;
-
- if (parent) {
- switch (parent.type) {
- case 'VariableDeclarator': {
- if (isUpperCase(parent.id.name)) return;
- break;
- }
- case 'Property': {
- // if node is key of property, skip
- if (parent.key === node) return;
- // name if key is Identifier; value if key is Literal
- // dont care whether if this is computed or not
- if (isUpperCase(parent.key.name || parent.key.value)) return;
- break;
- }
- case 'ImportDeclaration': // skip
- return;
- default:
- let LOOK_UP_LIMIT = 3;
- let temp = parent;
- while (temp && LOOK_UP_LIMIT > 0) {
- LOOK_UP_LIMIT--;
- if (temp.type === 'CallExpression') {
- // import(...) is valid
- if (temp.callee.type === 'Import') return;
-
- if (isValidFunctionCall(temp)) return;
- break;
- }
- temp = temp.parent;
- }
- break;
- }
- }
+ // allow statements like const a = "FOO"
+ if (isUpperCase(trimed)) return;
if (match(trimed)) return;
- context.report({
- node,
- message,
- fix(fixer) {
- return fixer.replaceText(node, `i18next.t('${node.value}')`);
- }
- });
+ context.report({ node, message });
}
}
};
@@ -137,25 +172,13 @@ module.exports = {
parserServices.defineTemplateBodyVisitor(
{
VText(node) {
- const trimed = node.value.trim();
- if (!trimed) return;
- if (match(trimed)) return;
- context.report({
- node,
- message,
- fix(fixer) {
- return fixer.replaceText(
- node,
- node.value.replace(
- /^(\s*)(.+?)(\s*)$/, // keep spaces
- "$1{{i18next.t('$2')}}$3"
- )
- );
- }
- });
+ scriptVisitor['JSXText'](node);
+ },
+ 'VExpressionContainer CallExpression Literal'(node) {
+ scriptVisitor['CallExpression Literal'](node);
},
- 'VExpressionContainer Literal'(node) {
- scriptVisitor.Literal(node);
+ 'VExpressionContainer Literal:exit'(node) {
+ Literal(node);
}
},
scriptVisitor
diff --git a/package-lock.json b/package-lock.json
index 6532873..b08c6d1 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -442,6 +442,64 @@
"rimraf": "^2.5.2"
}
},
+ "@types/eslint-visitor-keys": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
+ "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==",
+ "dev": true
+ },
+ "@typescript-eslint/experimental-utils": {
+ "version": "1.10.2",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-1.10.2.tgz",
+ "integrity": "sha512-Hf5lYcrnTH5Oc67SRrQUA7KuHErMvCf5RlZsyxXPIT6AXa8fKTyfFO6vaEnUmlz48RpbxO4f0fY3QtWkuHZNjg==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/typescript-estree": "1.10.2",
+ "eslint-scope": "^4.0.0"
+ },
+ "dependencies": {
+ "eslint-scope": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz",
+ "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==",
+ "dev": true,
+ "requires": {
+ "esrecurse": "^4.1.0",
+ "estraverse": "^4.1.1"
+ }
+ }
+ }
+ },
+ "@typescript-eslint/parser": {
+ "version": "1.10.2",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-1.10.2.tgz",
+ "integrity": "sha512-xWDWPfZfV0ENU17ermIUVEVSseBBJxKfqBcRCMZ8nAjJbfA5R7NWMZmFFHYnars5MjK4fPjhu4gwQv526oZIPQ==",
+ "dev": true,
+ "requires": {
+ "@types/eslint-visitor-keys": "^1.0.0",
+ "@typescript-eslint/experimental-utils": "1.10.2",
+ "@typescript-eslint/typescript-estree": "1.10.2",
+ "eslint-visitor-keys": "^1.0.0"
+ }
+ },
+ "@typescript-eslint/typescript-estree": {
+ "version": "1.10.2",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-1.10.2.tgz",
+ "integrity": "sha512-Kutjz0i69qraOsWeI8ETqYJ07tRLvD9URmdrMoF10bG8y8ucLmPtSxROvVejWvlJUGl2et/plnMiKRDW+rhEhw==",
+ "dev": true,
+ "requires": {
+ "lodash.unescape": "4.0.1",
+ "semver": "5.5.0"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
+ "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==",
+ "dev": true
+ }
+ }
+ },
"JSONStream": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz",
@@ -1839,6 +1897,12 @@
"lodash._reinterpolate": "~3.0.0"
}
},
+ "lodash.unescape": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz",
+ "integrity": "sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw=",
+ "dev": true
+ },
"log-symbols": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz",
@@ -2873,6 +2937,12 @@
"prelude-ls": "~1.1.2"
}
},
+ "typescript": {
+ "version": "3.5.2",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.2.tgz",
+ "integrity": "sha512-7KxJovlYhTX5RaRbUdkAXN1KUZ8PwWlTzQdHV6xNqvuFOs7+WBo10TQUqT19Q/Jz2hk5v9TQDIhyLhhJY4p5AA==",
+ "dev": true
+ },
"uri-js": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
diff --git a/package.json b/package.json
index 0fccead..a7a232c 100644
--- a/package.json
+++ b/package.json
@@ -28,10 +28,12 @@
"devDependencies": {
"@commitlint/cli": "^7.5.2",
"@commitlint/config-conventional": "^7.5.0",
+ "@typescript-eslint/parser": "^1.10.2",
"babel-eslint": "^10.0.1",
"eslint": "^5.16.0",
"husky": "^1.3.1",
"mocha": "^6.1.4",
+ "typescript": "^3.5.2",
"vue-eslint-parser": "^6.0.3"
},
"engines": {
diff --git a/tests/lib/rules/no-literal-string.js b/tests/lib/rules/no-literal-string.js
index b250197..6787e54 100644
--- a/tests/lib/rules/no-literal-string.js
+++ b/tests/lib/rules/no-literal-string.js
@@ -47,6 +47,8 @@ ruleTester.run('no-literal-string', rule, {
{ code: 'var a = {A_B: "hello world"};' },
{ code: 'var a = {foo: "FOO"};' },
// JSX
+ { code: '' },
+ { code: '' },
{ code: '{i18next.t("foo")}
' }
],
@@ -81,3 +83,29 @@ vueTester.run('no-literal-string', rule, {
}
]
});
+// ────────────────────────────────────────────────────────────────────────────────
+
+//
+// ─── TYPESCRIPT ─────────────────────────────────────────────────────────────────
+//
+
+const tsTester = new RuleTester({
+ parser: '@typescript-eslint/parser',
+ parserOptions: {
+ sourceType: 'module',
+ ecmaFeatures: {
+ jsx: true
+ }
+ }
+});
+
+tsTester.run('no-literal-string', rule, {
+ valid: [{ code: '' }],
+ invalid: [
+ {
+ code: `()`,
+ errors
+ }
+ ]
+});
+// ────────────────────────────────────────────────────────────────────────────────
From dd279c6dd4863a241c878d0e9d9238da90cd5b8b Mon Sep 17 00:00:00 2001
From: edvardchen <>
Date: Wed, 10 Jul 2019 15:10:56 +0800
Subject: [PATCH 2/7] fix: wrongly handle Literal node in VExpressionContainer
---
lib/rules/no-literal-string.js | 3 +--
tests/lib/rules/no-literal-string.js | 4 ++++
2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/lib/rules/no-literal-string.js b/lib/rules/no-literal-string.js
index 2dcccf2..3388def 100644
--- a/lib/rules/no-literal-string.js
+++ b/lib/rules/no-literal-string.js
@@ -83,7 +83,6 @@ module.exports = {
// Public
//----------------------------------------------------------------------
const visited = [];
- function Literal(node) {}
function getNearestAncestor(node, type) {
let temp = node.parent;
@@ -178,7 +177,7 @@ module.exports = {
scriptVisitor['CallExpression Literal'](node);
},
'VExpressionContainer Literal:exit'(node) {
- Literal(node);
+ scriptVisitor['Literal:exit'](node);
}
},
scriptVisitor
diff --git a/tests/lib/rules/no-literal-string.js b/tests/lib/rules/no-literal-string.js
index 6787e54..582ed1c 100644
--- a/tests/lib/rules/no-literal-string.js
+++ b/tests/lib/rules/no-literal-string.js
@@ -80,6 +80,10 @@ vueTester.run('no-literal-string', rule, {
{
code: 'abc',
errors
+ },
+ {
+ code: '{{"hello"}}',
+ errors
}
]
});
From 1527eae7d04eefcdaffac22afa4a04995a764622 Mon Sep 17 00:00:00 2001
From: edvardchen <>
Date: Wed, 10 Jul 2019 15:16:59 +0800
Subject: [PATCH 3/7] feat: dont check literal in export declaration
---
lib/rules/no-literal-string.js | 10 ++++++++++
tests/lib/rules/no-literal-string.js | 2 ++
2 files changed, 12 insertions(+)
diff --git a/lib/rules/no-literal-string.js b/lib/rules/no-literal-string.js
index 3388def..9210369 100644
--- a/lib/rules/no-literal-string.js
+++ b/lib/rules/no-literal-string.js
@@ -110,6 +110,16 @@ module.exports = {
visited.push(node);
},
+ 'ExportAllDeclaration Literal'(node) {
+ // allow export * from 'mod'
+ visited.push(node);
+ },
+
+ 'ExportNamedDeclaration Literal'(node) {
+ // allow export { named } from 'mod'
+ visited.push(node);
+ },
+
'VariableDeclarator > Literal'(node) {
// allow statements like const A_B = "test"
if (isUpperCase(node.parent.id.name)) visited.push(node);
diff --git a/tests/lib/rules/no-literal-string.js b/tests/lib/rules/no-literal-string.js
index 582ed1c..c7bf778 100644
--- a/tests/lib/rules/no-literal-string.js
+++ b/tests/lib/rules/no-literal-string.js
@@ -30,6 +30,8 @@ ruleTester.run('no-literal-string', rule, {
valid: [
{ code: 'import("hello")' },
{ code: 'import name from "hello";' },
+ { code: 'export * from "hello_export_all";' },
+ { code: 'export { a } from "hello_export";' },
{ code: 'require("hello");' },
{ code: 'const a = require(["hello"]);' },
{ code: 'const a = require(["hel" + "lo"]);' },
From 57c5066cd70b3b34976acb84a31d4ee43ceab060 Mon Sep 17 00:00:00 2001
From: edvardchen <>
Date: Wed, 10 Jul 2019 15:23:01 +0800
Subject: [PATCH 4/7] chore: reposition scriptVisitor selectors
---
lib/rules/no-literal-string.js | 58 ++++++++++++++++++++--------------
1 file changed, 34 insertions(+), 24 deletions(-)
diff --git a/lib/rules/no-literal-string.js b/lib/rules/no-literal-string.js
index 9210369..5e7391d 100644
--- a/lib/rules/no-literal-string.js
+++ b/lib/rules/no-literal-string.js
@@ -96,14 +96,9 @@ module.exports = {
}
const scriptVisitor = {
- 'JSXAttribute Literal'(node) {
- const parent = getNearestAncestor(node, 'JSXAttribute');
-
- // allow
- if (isValidAttrName(parent.name.name)) {
- visited.push(node);
- }
- },
+ //
+ // ─── EXPORT AND IMPORT ───────────────────────────────────────────
+ //
'ImportDeclaration Literal'(node) {
// allow (import abc form 'abc')
@@ -119,6 +114,37 @@ module.exports = {
// allow export { named } from 'mod'
visited.push(node);
},
+ // ─────────────────────────────────────────────────────────────────
+
+ //
+ // ─── JSX ─────────────────────────────────────────────────────────
+ //
+
+ 'JSXElement > Literal'(node) {
+ scriptVisitor.JSXText(node);
+ },
+
+ 'JSXAttribute Literal'(node) {
+ const parent = getNearestAncestor(node, 'JSXAttribute');
+
+ // allow
+ if (isValidAttrName(parent.name.name)) {
+ visited.push(node);
+ }
+ },
+
+ // @typescript-eslint/parser would parse string literal as JSXText node
+ JSXText(node) {
+ const trimed = node.value.trim();
+ visited.push(node);
+
+ if (!trimed || match(trimed)) {
+ return;
+ }
+
+ context.report({ node, message });
+ },
+ // ─────────────────────────────────────────────────────────────────
'VariableDeclarator > Literal'(node) {
// allow statements like const A_B = "test"
@@ -143,22 +169,6 @@ module.exports = {
if (isValidFunctionCall(parent)) visited.push(node);
},
- 'JSXElement > Literal'(node) {
- scriptVisitor.JSXText(node);
- },
-
- // @typescript-eslint/parser would parse string literal as JSXText node
- JSXText(node) {
- const trimed = node.value.trim();
- visited.push(node);
-
- if (!trimed || match(trimed)) {
- return;
- }
-
- context.report({ node, message });
- },
-
'Literal:exit'(node) {
if (visited.includes(node)) return;
From fd93861fb5728f3bb6a5354f7141584fbcf76d2d Mon Sep 17 00:00:00 2001
From: edvardchen <>
Date: Wed, 10 Jul 2019 15:27:16 +0800
Subject: [PATCH 5/7] feat: dont check TSLiteralType
---
lib/rules/no-literal-string.js | 10 ++++++++++
tests/lib/rules/no-literal-string.js | 10 +++++++++-
2 files changed, 19 insertions(+), 1 deletion(-)
diff --git a/lib/rules/no-literal-string.js b/lib/rules/no-literal-string.js
index 5e7391d..bd5690b 100644
--- a/lib/rules/no-literal-string.js
+++ b/lib/rules/no-literal-string.js
@@ -146,6 +146,16 @@ module.exports = {
},
// ─────────────────────────────────────────────────────────────────
+ //
+ // ─── TYPESCRIPT ──────────────────────────────────────────────────
+ //
+
+ 'TSLiteralType Literal'(node) {
+ // allow var a: Type['member'];
+ visited.push(node);
+ },
+ // ─────────────────────────────────────────────────────────────────
+
'VariableDeclarator > Literal'(node) {
// allow statements like const A_B = "test"
if (isUpperCase(node.parent.id.name)) visited.push(node);
diff --git a/tests/lib/rules/no-literal-string.js b/tests/lib/rules/no-literal-string.js
index c7bf778..365945e 100644
--- a/tests/lib/rules/no-literal-string.js
+++ b/tests/lib/rules/no-literal-string.js
@@ -106,7 +106,15 @@ const tsTester = new RuleTester({
});
tsTester.run('no-literal-string', rule, {
- valid: [{ code: '' }],
+ valid: [
+ { code: '' },
+ {
+ code: "var a: Element['nodeName']"
+ },
+ {
+ code: "var a: Omit"
+ }
+ ],
invalid: [
{
code: `()`,
From ee23dd77cb093a65003cba742acb88b6fa868023 Mon Sep 17 00:00:00 2001
From: edvardchen <>
Date: Wed, 10 Jul 2019 15:43:07 +0800
Subject: [PATCH 6/7] docs: update
---
README.md | 44 +++++++++++++++++---------------------------
1 file changed, 17 insertions(+), 27 deletions(-)
diff --git a/README.md b/README.md
index 89072df..1490d83 100644
--- a/README.md
+++ b/README.md
@@ -41,33 +41,7 @@ or
This rule aims to avoid developers to display literal string to users
in those projects which need to support [multi-language](https://www.i18next.com/).
-The `--fix` option on the [command line](https://eslint.org/docs/user-guide/command-line-interface#fixing-problems) can automatically fix some of the problems reported by this rule.
-
-All literal strings in html template are typically mistakes. For JSX example:
-
-```HTML
-foo
-```
-
-They should be translated by [i18next translation api](https://www.i18next.com/):
-
-```HTML
-{i18next.t('foo')}
-```
-
-Same for [Vue template](https://vuejs.org/v2/guide/syntax.html):
-
-```vue
-
-
- foo
-
-
-
-
- {{ i18next.t('foo') }}
-
-```
+> Note: Disable auto-fix because key in the call `i18next.t(key)` ussally was not the same as the literal
For plain javascript, literal strings that are not constant string (all characters are `UPPERCASE`) are disallowed:
@@ -152,6 +126,22 @@ var bar = store.dispatch('bar');
var bar2 = store.commit('bar');
```
+#### MISC
+
+The following cases would be skip default:
+
+```typescript
+import mod from 'm';
+import('mod');
+require('mod');
+
+export { named } from 'm';
+export * from 'm';
+
+var a: Type['member'];
+var a: Omit;
+```
+
### Options
#### ignore
From 4d762e91e1155bb72917256b8ab73816f1b2cde5 Mon Sep 17 00:00:00 2001
From: edvardchen <>
Date: Wed, 10 Jul 2019 21:13:32 +0800
Subject: [PATCH 7/7] chore(release): 2.0.0
---
CHANGELOG.md | 25 +++++++++++++++++++++++++
package-lock.json | 2 +-
package.json | 2 +-
3 files changed, 27 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8ab99af..eec88e4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,31 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
+## [2.0.0](https://github.com/edvardchen/eslint-plugin-i18next/compare/v1.2.0...v2.0.0) (2019-07-10)
+
+
+### Bug Fixes
+
+* wrongly handle Literal node in VExpressionContainer ([dd279c6](https://github.com/edvardchen/eslint-plugin-i18next/commit/dd279c6))
+
+
+### Features
+
+* dont check literal in export declaration ([1527eae](https://github.com/edvardchen/eslint-plugin-i18next/commit/1527eae))
+* dont check TSLiteralType ([fd93861](https://github.com/edvardchen/eslint-plugin-i18next/commit/fd93861))
+
+
+### refactor
+
+* use rule selectors to reduce code complexity ([28d73ff](https://github.com/edvardchen/eslint-plugin-i18next/commit/28d73ff))
+
+
+### BREAKING CHANGES
+
+* Disable fix because key in the call i18next.t(key) ussally was not same as the plain text
+
+
+
## [1.2.0](https://github.com/edvardchen/eslint-plugin-i18next/compare/v1.1.3...v1.2.0) (2019-06-20)
diff --git a/package-lock.json b/package-lock.json
index b08c6d1..46914ef 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "eslint-plugin-i18next",
- "version": "1.2.0",
+ "version": "2.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
diff --git a/package.json b/package.json
index a7a232c..5067de7 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "eslint-plugin-i18next",
- "version": "1.2.0",
+ "version": "2.0.0",
"description": "ESLint plugin for i18n",
"keywords": [
"eslint",