Skip to content

Commit 7612dfd

Browse files
committed
⭐ new(rule): add no-dynamic-keys rule
1 parent 18b77ed commit 7612dfd

File tree

6 files changed

+238
-1
lines changed

6 files changed

+238
-1
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Details changes for each release are documented in the [CHANGELOG.md](https://gi
1717

1818
## :white_check_mark: TODO
1919
- [x] no-missing-keys
20-
- [ ] no-dynamic-keys
20+
- [x] no-dynamic-keys
2121
- [x] no-unused-keys
2222
- [ ] no-raw-text
2323
- [ ] valid-message-syntax

docs/rules/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,6 @@
1313

1414
| Rule ID | Description | |
1515
|:--------|:------------|:---|
16+
| [vue-i18n/<wbr>no-dynamic-keys](./no-dynamic-keys.html) | disallow localization dynamic keys at localization methods | |
1617
| [vue-i18n/<wbr>no-unused-keys](./no-unused-keys.html) | disallow unused localization keys | |
1718

docs/rules/no-dynamic-keys.md

+99
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# vue-i18n/no-dynamic-keys
2+
3+
> disallow localization dynamic keys at localization methods
4+
5+
This rule warns localization dynamic keys.
6+
7+
This role is useful for unifying all static keys in your application.
8+
9+
## :book: Rule Details
10+
11+
You can be detected with this rule the following:
12+
13+
- `$t`
14+
- `t`
15+
- `$tc`
16+
- `tc`
17+
- `v-t`
18+
19+
:-1: Examples of **incorrect** code for this rule:
20+
21+
locale messages:
22+
```json
23+
{
24+
"hello": "Hello! DIO!"
25+
}
26+
```
27+
28+
localization codes:
29+
30+
```vue
31+
<template>
32+
<div class="app">
33+
<!-- ✗ BAD -->
34+
<p>{{ $t(msg) }}</p>
35+
<!-- ✗ BAD -->
36+
<p v-t="msg"></p>
37+
</div>
38+
</template>
39+
```
40+
41+
```js
42+
const i18n = new VueI18n({
43+
locale: 'en',
44+
messages: {
45+
en: require('./locales/en.json')
46+
}
47+
})
48+
49+
const app = new Vue({
50+
i18n,
51+
data () {
52+
return { msg: 'hello' }
53+
}
54+
})
55+
56+
/* ✗ BAD */
57+
i18n.t(app.msg)
58+
```
59+
60+
:+1: Examples of **correct** code for this rule:
61+
62+
locale messages:
63+
```json
64+
{
65+
"hello": "Hello! DIO!"
66+
}
67+
```
68+
69+
localization codes:
70+
71+
```vue
72+
<template>
73+
<div class="app">
74+
<!-- ✗ GOOD -->
75+
<p>{{ $t('hello') }}</p>
76+
<!-- ✗ GOOD -->
77+
<p v-t="'hello'"></p>
78+
</div>
79+
</template>
80+
```
81+
82+
```js
83+
const i18n = new VueI18n({
84+
locale: 'en',
85+
messages: {
86+
en: require('./locales/en.json')
87+
}
88+
})
89+
90+
const app = new Vue({
91+
i18n,
92+
data () {
93+
return { msg: 'hello' }
94+
}
95+
})
96+
97+
/* ✓ GOOD */
98+
i18n.t('hello')
99+
```

lib/rules.js

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
'use strict'
33

44
module.exports = {
5+
'no-dynamic-keys': require('./rules/no-dynamic-keys'),
56
'no-missing-keys': require('./rules/no-missing-keys'),
67
'no-unused-keys': require('./rules/no-unused-keys')
78
}

lib/rules/no-dynamic-keys.js

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/**
2+
* @author kazuya kawaguchi (a.k.a. kazupon)
3+
*/
4+
'use strict'
5+
6+
const { defineTemplateBodyVisitor } = require('../utils/index')
7+
8+
function checkDirective (context, node) {
9+
if ((node.value && node.value.type === 'VExpressionContainer') &&
10+
(node.value.expression && node.value.expression.type === 'Identifier')) {
11+
const name = node.value.expression.name
12+
context.report({
13+
node,
14+
message: `'${name}' dynamic key is used'`
15+
})
16+
}
17+
}
18+
19+
function checkCallExpression (context, node) {
20+
const funcName = (node.callee.type === 'MemberExpression' && node.callee.property.name) || node.callee.name
21+
22+
if (!/^(\$t|t|\$tc|tc)$/.test(funcName) || !node.arguments || !node.arguments.length) {
23+
return
24+
}
25+
26+
const [keyNode] = node.arguments
27+
if (keyNode.type === 'Identifier') {
28+
const name = keyNode.name
29+
context.report({
30+
node,
31+
message: `'${name}' dynamic key is used'`
32+
})
33+
}
34+
}
35+
36+
function create (context) {
37+
return defineTemplateBodyVisitor(context, {
38+
"VAttribute[directive=true][key.name='t']" (node) {
39+
checkDirective(context, node)
40+
},
41+
42+
"VAttribute[directive=true][key.name.name='t']" (node) {
43+
checkDirective(context, node)
44+
},
45+
46+
CallExpression (node) {
47+
checkCallExpression(context, node)
48+
}
49+
}, {
50+
CallExpression (node) {
51+
checkCallExpression(context, node)
52+
}
53+
})
54+
}
55+
56+
module.exports = {
57+
meta: {
58+
type: 'suggestion',
59+
docs: {
60+
description: 'disallow localization dynamic keys at localization methods',
61+
category: 'Best Practices',
62+
recommended: false
63+
},
64+
fixable: null,
65+
schema: []
66+
},
67+
create
68+
}

tests/lib/rules/no-dynamic-keys.js

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/**
2+
* @author kazuya kawaguchi (a.k.a. kazupon)
3+
*/
4+
'use strict'
5+
6+
const RuleTester = require('eslint').RuleTester
7+
const rule = require('../../../lib/rules/no-dynamic-keys')
8+
9+
const tester = new RuleTester({
10+
parser: 'vue-eslint-parser',
11+
parserOptions: { ecmaVersion: 2015 }
12+
})
13+
14+
tester.run('no-dynamic-keys', rule, {
15+
valid: [{
16+
// basic key
17+
code: `$t('hello')`
18+
}, {
19+
// nested key
20+
code: `t('messages.nested.hello')`
21+
}, {
22+
// linked key
23+
code: `$tc('messages.hello.link')`
24+
}, {
25+
// hypened key
26+
code: `tc('hello-dio')`
27+
}, {
28+
// key like the message
29+
code: `$t('hello {name}')`
30+
}, {
31+
// instance member
32+
code: `i18n.t('hello {name}')`
33+
}, {
34+
// using mustaches in template block
35+
code: `<template>
36+
<p>{{ $t('hello') }}</p>
37+
</template>`
38+
}, {
39+
// using custom directive in template block
40+
code: `<template>
41+
<p v-t="'hello'"></p>
42+
</template>`
43+
}],
44+
45+
invalid: [{
46+
// basic
47+
code: `$t(missing)`,
48+
errors: [
49+
`'missing' dynamic key is used'`
50+
]
51+
}, {
52+
// using mustaches in template block
53+
code: `<template>
54+
<p>{{ $t(missing) }}</p>
55+
</template>`,
56+
errors: [
57+
`'missing' dynamic key is used'`
58+
]
59+
}, {
60+
// using custom directive in template block
61+
code: `<template>
62+
<p v-t="missing"></p>
63+
</template>`,
64+
errors: [
65+
`'missing' dynamic key is used'`
66+
]
67+
}]
68+
})

0 commit comments

Comments
 (0)