@18ways/eslint-plugin-translate 0.0.0-alpha.1aa37fb04f1f
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +215 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +63 -0
- package/dist/index.js.map +1 -0
- package/dist/rules/require-translation.d.ts +12 -0
- package/dist/rules/require-translation.d.ts.map +1 -0
- package/dist/rules/require-translation.js +260 -0
- package/dist/rules/require-translation.js.map +1 -0
- package/package.json +49 -0
package/README.md
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
# @18ways/eslint-plugin-translate
|
|
2
|
+
|
|
3
|
+
ESLint plugin to detect untranslated strings that should use 18ways translate functionality.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun add -D @18ways/eslint-plugin-translate
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
Add `@18ways/translate` to your ESLint configuration:
|
|
14
|
+
|
|
15
|
+
### ESLint Configuration (`.eslintrc.js`)
|
|
16
|
+
|
|
17
|
+
```javascript
|
|
18
|
+
module.exports = {
|
|
19
|
+
plugins: ['@18ways/translate'],
|
|
20
|
+
rules: {
|
|
21
|
+
'@18ways/translate/require-translation': 'error',
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Using Preset Configurations
|
|
27
|
+
|
|
28
|
+
#### Recommended Configuration
|
|
29
|
+
|
|
30
|
+
```javascript
|
|
31
|
+
module.exports = {
|
|
32
|
+
extends: ['plugin:@18ways/translate/recommended'],
|
|
33
|
+
};
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
#### Strict Configuration
|
|
37
|
+
|
|
38
|
+
```javascript
|
|
39
|
+
module.exports = {
|
|
40
|
+
extends: ['plugin:@18ways/translate/strict'],
|
|
41
|
+
};
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Rule: `require-translation`
|
|
45
|
+
|
|
46
|
+
Detects user-facing strings that should be wrapped in 18ways translation components or hooks.
|
|
47
|
+
|
|
48
|
+
### What it detects:
|
|
49
|
+
|
|
50
|
+
1. **JSX Text Content** - Text directly in JSX elements
|
|
51
|
+
2. **User-facing Attributes** - Attributes like `alt`, `placeholder`, `aria-label`, etc.
|
|
52
|
+
3. **String Literals** - Hardcoded strings in user-facing contexts
|
|
53
|
+
4. **Template Literals** - Template strings with user-facing content
|
|
54
|
+
|
|
55
|
+
### Examples
|
|
56
|
+
|
|
57
|
+
#### ❌ Invalid (will trigger the rule):
|
|
58
|
+
|
|
59
|
+
```tsx
|
|
60
|
+
// JSX text content
|
|
61
|
+
<div>Hello World</div>
|
|
62
|
+
<button>Click Me</button>
|
|
63
|
+
|
|
64
|
+
// User-facing attributes
|
|
65
|
+
<img src="/logo.png" alt="Company Logo" />
|
|
66
|
+
<input placeholder="Enter your name" />
|
|
67
|
+
<button aria-label="Close dialog">×</button>
|
|
68
|
+
|
|
69
|
+
// String literals in JSX
|
|
70
|
+
<div>{"Welcome to our site"}</div>
|
|
71
|
+
|
|
72
|
+
// Template literals
|
|
73
|
+
<div>{`Hello ${name}!`}</div>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
#### ✅ Valid (properly translated):
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
// Using <T> component
|
|
80
|
+
<T>Hello World</T>
|
|
81
|
+
<button><T>Click Me</T></button>
|
|
82
|
+
|
|
83
|
+
// Using useT hook
|
|
84
|
+
const t = useT();
|
|
85
|
+
const message = t("Hello World");
|
|
86
|
+
|
|
87
|
+
// Non-user-facing content (automatically ignored)
|
|
88
|
+
<div id="my-component" className="header">
|
|
89
|
+
<img src="/images/logo.png" />
|
|
90
|
+
<div>{42}</div>
|
|
91
|
+
<a href="mailto:test@example.com">
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Rule Options
|
|
95
|
+
|
|
96
|
+
```javascript
|
|
97
|
+
{
|
|
98
|
+
"@18ways/translate/require-translation": ["error", {
|
|
99
|
+
"translateComponent": "T", // Name of translation component (default: "T")
|
|
100
|
+
"translateHook": "useT", // Name of translation hook (default: "useT")
|
|
101
|
+
"userFacingAttributes": [ // Attributes considered user-facing
|
|
102
|
+
"alt", "title", "aria-label",
|
|
103
|
+
"placeholder", "value", "label"
|
|
104
|
+
],
|
|
105
|
+
"ignorePatterns": [ // Regex patterns to ignore
|
|
106
|
+
"^[a-zA-Z0-9_-]{1,3}$", // Very short identifiers (1-3 chars)
|
|
107
|
+
"^[A-Z_]+$", // Constants
|
|
108
|
+
"^\\d+$", // Numbers
|
|
109
|
+
"^[a-z]+://.*", // URLs
|
|
110
|
+
"^/.*", // Paths
|
|
111
|
+
"^#[a-fA-F0-9]{3,8}$", // Hex colors
|
|
112
|
+
"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$", // Emails
|
|
113
|
+
"^\\s*$" // Whitespace only
|
|
114
|
+
],
|
|
115
|
+
"ignoreFiles": [ // File patterns to ignore
|
|
116
|
+
".*\\.test\\.[jt]sx?$",
|
|
117
|
+
".*\\.spec\\.[jt]sx?$",
|
|
118
|
+
".*/test/.*"
|
|
119
|
+
]
|
|
120
|
+
}]
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Custom Translation Component
|
|
125
|
+
|
|
126
|
+
If you use a custom translation component name:
|
|
127
|
+
|
|
128
|
+
```javascript
|
|
129
|
+
{
|
|
130
|
+
"@18ways/translate/require-translation": ["error", {
|
|
131
|
+
"translateComponent": "Translate"
|
|
132
|
+
}]
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Then use it like:
|
|
137
|
+
|
|
138
|
+
```tsx
|
|
139
|
+
<Translate>Hello World</Translate>
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Additional User-facing Attributes
|
|
143
|
+
|
|
144
|
+
To check additional attributes:
|
|
145
|
+
|
|
146
|
+
```javascript
|
|
147
|
+
{
|
|
148
|
+
"@18ways/translate/require-translation": ["error", {
|
|
149
|
+
"userFacingAttributes": [
|
|
150
|
+
"alt", "title", "aria-label", "placeholder",
|
|
151
|
+
"data-tooltip", "summary", "caption"
|
|
152
|
+
]
|
|
153
|
+
}]
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Custom Ignore Patterns
|
|
158
|
+
|
|
159
|
+
To ignore specific patterns:
|
|
160
|
+
|
|
161
|
+
```javascript
|
|
162
|
+
{
|
|
163
|
+
"@18ways/translate/require-translation": ["error", {
|
|
164
|
+
"ignorePatterns": [
|
|
165
|
+
"^[a-zA-Z0-9_-]+$", // Default patterns
|
|
166
|
+
"^TEST_.*", // Custom: ignore test constants
|
|
167
|
+
"^\\$.*" // Custom: ignore $ prefixed strings
|
|
168
|
+
]
|
|
169
|
+
}]
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Integration with 18ways
|
|
174
|
+
|
|
175
|
+
This plugin is designed to work with the 18ways translation system:
|
|
176
|
+
|
|
177
|
+
```tsx
|
|
178
|
+
import { Ways, T, useT } from '18ways';
|
|
179
|
+
|
|
180
|
+
// Wrap your app
|
|
181
|
+
<Ways apiKey="your-api-key" locale="en-US" baseLocale="en-US">
|
|
182
|
+
<Ways context="header">
|
|
183
|
+
<T>Welcome to our site</T>
|
|
184
|
+
</Ways>
|
|
185
|
+
</Ways>;
|
|
186
|
+
|
|
187
|
+
// Use translation hook
|
|
188
|
+
function MyComponent() {
|
|
189
|
+
const t = useT();
|
|
190
|
+
return <p>{t('Learn more about our company')}</p>;
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Development
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
# Install dependencies
|
|
198
|
+
bun install
|
|
199
|
+
|
|
200
|
+
# Build the plugin
|
|
201
|
+
bun run build
|
|
202
|
+
|
|
203
|
+
# Run tests
|
|
204
|
+
bun run test
|
|
205
|
+
|
|
206
|
+
# Run tests in watch mode
|
|
207
|
+
bun run test:watch
|
|
208
|
+
|
|
209
|
+
# Lint the code
|
|
210
|
+
bun run lint
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## License
|
|
214
|
+
|
|
215
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
declare const plugin: {
|
|
2
|
+
meta: {
|
|
3
|
+
name: string;
|
|
4
|
+
version: string;
|
|
5
|
+
};
|
|
6
|
+
rules: {
|
|
7
|
+
'require-translation': import("@typescript-eslint/utils/ts-eslint").RuleModule<"untranslatedText" | "untranslatedAttribute" | "untranslatedStringLiteral" | "untranslatedTemplateLiteral", [import("./rules/require-translation").Options], import("@typescript-eslint/utils/ts-eslint").RuleListener>;
|
|
8
|
+
};
|
|
9
|
+
configs: {
|
|
10
|
+
recommended: {
|
|
11
|
+
plugins: string[];
|
|
12
|
+
rules: {
|
|
13
|
+
'@18ways/translate/require-translation': string;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
strict: {
|
|
17
|
+
plugins: string[];
|
|
18
|
+
rules: {
|
|
19
|
+
'@18ways/translate/require-translation': (string | {
|
|
20
|
+
translateComponent: string;
|
|
21
|
+
translateHook: string;
|
|
22
|
+
userFacingAttributes: string[];
|
|
23
|
+
ignorePatterns: string[];
|
|
24
|
+
ignoreFiles: string[];
|
|
25
|
+
})[];
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
export = plugin;
|
|
31
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,QAAA,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0DX,CAAC;AAEF,SAAS,MAAM,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const require_translation_1 = require("./rules/require-translation");
|
|
3
|
+
const plugin = {
|
|
4
|
+
meta: {
|
|
5
|
+
name: '@18ways/eslint-plugin-translate',
|
|
6
|
+
version: '1.0.0',
|
|
7
|
+
},
|
|
8
|
+
rules: {
|
|
9
|
+
'require-translation': require_translation_1.requireTranslation,
|
|
10
|
+
},
|
|
11
|
+
configs: {
|
|
12
|
+
recommended: {
|
|
13
|
+
plugins: ['@18ways/translate'],
|
|
14
|
+
rules: {
|
|
15
|
+
'@18ways/translate/require-translation': 'error',
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
strict: {
|
|
19
|
+
plugins: ['@18ways/translate'],
|
|
20
|
+
rules: {
|
|
21
|
+
'@18ways/translate/require-translation': [
|
|
22
|
+
'error',
|
|
23
|
+
{
|
|
24
|
+
translateComponent: 'T',
|
|
25
|
+
translateHook: 'useT',
|
|
26
|
+
userFacingAttributes: [
|
|
27
|
+
'alt',
|
|
28
|
+
'title',
|
|
29
|
+
'aria-label',
|
|
30
|
+
'aria-labelledby',
|
|
31
|
+
'aria-describedby',
|
|
32
|
+
'placeholder',
|
|
33
|
+
'value',
|
|
34
|
+
'defaultValue',
|
|
35
|
+
'label',
|
|
36
|
+
'summary',
|
|
37
|
+
'caption',
|
|
38
|
+
],
|
|
39
|
+
ignorePatterns: [
|
|
40
|
+
'^[a-zA-Z0-9_-]{1,3}$',
|
|
41
|
+
'^[A-Z_]+$',
|
|
42
|
+
'^\\d+$',
|
|
43
|
+
'^[a-z]+://.*',
|
|
44
|
+
'^/.*',
|
|
45
|
+
'^#[a-fA-F0-9]{3,8}$',
|
|
46
|
+
'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$',
|
|
47
|
+
'^\\s*$',
|
|
48
|
+
],
|
|
49
|
+
ignoreFiles: [
|
|
50
|
+
'.*\\.test\\.[jt]sx?$',
|
|
51
|
+
'.*\\.spec\\.[jt]sx?$',
|
|
52
|
+
'.*/test/.*',
|
|
53
|
+
'.*/tests/.*',
|
|
54
|
+
'.*/__tests__/.*',
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
module.exports = plugin;
|
|
63
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,qEAAiE;AAEjE,MAAM,MAAM,GAAG;IACb,IAAI,EAAE;QACJ,IAAI,EAAE,iCAAiC;QACvC,OAAO,EAAE,OAAO;KACjB;IACD,KAAK,EAAE;QACL,qBAAqB,EAAE,wCAAkB;KAC1C;IACD,OAAO,EAAE;QACP,WAAW,EAAE;YACX,OAAO,EAAE,CAAC,mBAAmB,CAAC;YAC9B,KAAK,EAAE;gBACL,uCAAuC,EAAE,OAAO;aACjD;SACF;QACD,MAAM,EAAE;YACN,OAAO,EAAE,CAAC,mBAAmB,CAAC;YAC9B,KAAK,EAAE;gBACL,uCAAuC,EAAE;oBACvC,OAAO;oBACP;wBACE,kBAAkB,EAAE,GAAG;wBACvB,aAAa,EAAE,MAAM;wBACrB,oBAAoB,EAAE;4BACpB,KAAK;4BACL,OAAO;4BACP,YAAY;4BACZ,iBAAiB;4BACjB,kBAAkB;4BAClB,aAAa;4BACb,OAAO;4BACP,cAAc;4BACd,OAAO;4BACP,SAAS;4BACT,SAAS;yBACV;wBACD,cAAc,EAAE;4BACd,sBAAsB;4BACtB,WAAW;4BACX,QAAQ;4BACR,cAAc;4BACd,MAAM;4BACN,qBAAqB;4BACrB,mDAAmD;4BACnD,QAAQ;yBACT;wBACD,WAAW,EAAE;4BACX,sBAAsB;4BACtB,sBAAsB;4BACtB,YAAY;4BACZ,aAAa;4BACb,iBAAiB;yBAClB;qBACF;iBACF;aACF;SACF;KACF;CACF,CAAC;AAEF,iBAAS,MAAM,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
+
export interface Options {
|
|
3
|
+
translateComponent?: string;
|
|
4
|
+
translateHook?: string;
|
|
5
|
+
userFacingAttributes?: string[];
|
|
6
|
+
ignorePatterns?: string[];
|
|
7
|
+
ignoreFiles?: string[];
|
|
8
|
+
}
|
|
9
|
+
type MessageIds = 'untranslatedText' | 'untranslatedAttribute' | 'untranslatedStringLiteral' | 'untranslatedTemplateLiteral';
|
|
10
|
+
export declare const requireTranslation: ESLintUtils.RuleModule<MessageIds, [Options], ESLintUtils.RuleListener>;
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=require-translation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"require-translation.d.ts","sourceRoot":"","sources":["../../src/rules/require-translation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAY,MAAM,0BAA0B,CAAC;AAMjE,MAAM,WAAW,OAAO;IACtB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAChC,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,KAAK,UAAU,GACX,kBAAkB,GAClB,uBAAuB,GACvB,2BAA2B,GAC3B,6BAA6B,CAAC;AAqBlC,eAAO,MAAM,kBAAkB,yEAgQ7B,CAAC"}
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.requireTranslation = void 0;
|
|
4
|
+
const utils_1 = require("@typescript-eslint/utils");
|
|
5
|
+
const createRule = utils_1.ESLintUtils.RuleCreator((name) => `https://github.com/18ways/eslint-plugin-translate/blob/main/docs/rules/${name}.md`);
|
|
6
|
+
const DEFAULT_USER_FACING_ATTRIBUTES = [
|
|
7
|
+
'alt',
|
|
8
|
+
'title',
|
|
9
|
+
'aria-label',
|
|
10
|
+
'aria-labelledby',
|
|
11
|
+
'placeholder',
|
|
12
|
+
'label',
|
|
13
|
+
];
|
|
14
|
+
const DEFAULT_IGNORE_PATTERNS = [
|
|
15
|
+
'^.{1,3}$', // Very short strings (1-3 chars like IDs, icons, symbols)
|
|
16
|
+
'^[A-Z_]+$', // Constants (ALL_CAPS)
|
|
17
|
+
'^\\d+$', // Numbers only
|
|
18
|
+
'^[a-z]+://.*', // URLs
|
|
19
|
+
'^/.*', // Paths
|
|
20
|
+
'^#[a-fA-F0-9]{3,8}$', // Hex colors
|
|
21
|
+
'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$', // Emails
|
|
22
|
+
];
|
|
23
|
+
exports.requireTranslation = createRule({
|
|
24
|
+
name: 'require-translation',
|
|
25
|
+
meta: {
|
|
26
|
+
type: 'suggestion',
|
|
27
|
+
docs: {
|
|
28
|
+
description: 'Require user-facing strings to be wrapped in translation components',
|
|
29
|
+
},
|
|
30
|
+
messages: {
|
|
31
|
+
untranslatedText: 'User-facing text should be wrapped in a <{{translateComponent}}> component: "{{text}}"',
|
|
32
|
+
untranslatedAttribute: 'User-facing attribute "{{attribute}}" should use translated content: "{{text}}"',
|
|
33
|
+
untranslatedStringLiteral: 'User-facing string literal should be translated: "{{text}}"',
|
|
34
|
+
untranslatedTemplateLiteral: 'User-facing template literal should be translated: "{{text}}"',
|
|
35
|
+
},
|
|
36
|
+
schema: [
|
|
37
|
+
{
|
|
38
|
+
type: 'object',
|
|
39
|
+
properties: {
|
|
40
|
+
translateComponent: {
|
|
41
|
+
type: 'string',
|
|
42
|
+
default: 'T',
|
|
43
|
+
},
|
|
44
|
+
translateHook: {
|
|
45
|
+
type: 'string',
|
|
46
|
+
default: 'useT',
|
|
47
|
+
},
|
|
48
|
+
userFacingAttributes: {
|
|
49
|
+
type: 'array',
|
|
50
|
+
items: { type: 'string' },
|
|
51
|
+
default: DEFAULT_USER_FACING_ATTRIBUTES,
|
|
52
|
+
},
|
|
53
|
+
ignorePatterns: {
|
|
54
|
+
type: 'array',
|
|
55
|
+
items: { type: 'string' },
|
|
56
|
+
default: DEFAULT_IGNORE_PATTERNS,
|
|
57
|
+
},
|
|
58
|
+
ignoreFiles: {
|
|
59
|
+
type: 'array',
|
|
60
|
+
items: { type: 'string' },
|
|
61
|
+
default: [],
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
additionalProperties: false,
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
},
|
|
68
|
+
defaultOptions: [
|
|
69
|
+
{
|
|
70
|
+
translateComponent: 'T',
|
|
71
|
+
translateHook: 'useT',
|
|
72
|
+
userFacingAttributes: DEFAULT_USER_FACING_ATTRIBUTES,
|
|
73
|
+
ignorePatterns: DEFAULT_IGNORE_PATTERNS,
|
|
74
|
+
ignoreFiles: [],
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
create(context, [options]) {
|
|
78
|
+
const translateComponent = options.translateComponent || 'T';
|
|
79
|
+
const userFacingAttributes = options.userFacingAttributes || DEFAULT_USER_FACING_ATTRIBUTES;
|
|
80
|
+
const ignorePatterns = options.ignorePatterns || DEFAULT_IGNORE_PATTERNS;
|
|
81
|
+
const ignoreFiles = options.ignoreFiles || [];
|
|
82
|
+
const filename = context.getFilename();
|
|
83
|
+
// Skip files that match ignore patterns
|
|
84
|
+
if (ignoreFiles.some((pattern) => new RegExp(pattern).test(filename))) {
|
|
85
|
+
return {};
|
|
86
|
+
}
|
|
87
|
+
function shouldIgnoreText(text) {
|
|
88
|
+
if (!text || typeof text !== 'string')
|
|
89
|
+
return true;
|
|
90
|
+
// Trim whitespace for checking
|
|
91
|
+
const trimmed = text.trim();
|
|
92
|
+
if (!trimmed)
|
|
93
|
+
return true;
|
|
94
|
+
// Check against ignore patterns
|
|
95
|
+
return ignorePatterns.some((pattern) => new RegExp(pattern).test(trimmed));
|
|
96
|
+
}
|
|
97
|
+
function isInsideTranslateComponent(node) {
|
|
98
|
+
let parent = node.parent;
|
|
99
|
+
while (parent) {
|
|
100
|
+
if (parent.type === 'JSXElement') {
|
|
101
|
+
const openingElement = parent.openingElement;
|
|
102
|
+
if (openingElement.name.type === 'JSXIdentifier' &&
|
|
103
|
+
openingElement.name.name === translateComponent) {
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
parent = parent.parent;
|
|
108
|
+
}
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
function isWithinTFunction(node) {
|
|
112
|
+
let parent = node.parent;
|
|
113
|
+
while (parent) {
|
|
114
|
+
if (parent.type === 'CallExpression') {
|
|
115
|
+
const callee = parent.callee;
|
|
116
|
+
if (callee.type === 'Identifier' && callee.name === 't') {
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
parent = parent.parent;
|
|
121
|
+
}
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
function containsJSXExpressions(text) {
|
|
125
|
+
return /\{.*\}/.test(text);
|
|
126
|
+
}
|
|
127
|
+
return {
|
|
128
|
+
JSXText(node) {
|
|
129
|
+
const text = node.value;
|
|
130
|
+
if (shouldIgnoreText(text))
|
|
131
|
+
return;
|
|
132
|
+
if (isInsideTranslateComponent(node))
|
|
133
|
+
return;
|
|
134
|
+
if (containsJSXExpressions(text))
|
|
135
|
+
return;
|
|
136
|
+
context.report({
|
|
137
|
+
node,
|
|
138
|
+
messageId: 'untranslatedText',
|
|
139
|
+
data: {
|
|
140
|
+
translateComponent,
|
|
141
|
+
text: text.trim(),
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
},
|
|
145
|
+
JSXAttribute(node) {
|
|
146
|
+
if (!node.name || node.name.type !== 'JSXIdentifier')
|
|
147
|
+
return;
|
|
148
|
+
const attributeName = node.name.name;
|
|
149
|
+
if (!userFacingAttributes.includes(attributeName))
|
|
150
|
+
return;
|
|
151
|
+
const value = node.value;
|
|
152
|
+
if (!value)
|
|
153
|
+
return;
|
|
154
|
+
if (value.type === 'Literal' && typeof value.value === 'string') {
|
|
155
|
+
const text = value.value;
|
|
156
|
+
if (shouldIgnoreText(text))
|
|
157
|
+
return;
|
|
158
|
+
context.report({
|
|
159
|
+
node: value,
|
|
160
|
+
messageId: 'untranslatedAttribute',
|
|
161
|
+
data: {
|
|
162
|
+
attribute: attributeName,
|
|
163
|
+
text,
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
else if (value.type === 'JSXExpressionContainer') {
|
|
168
|
+
// Check if the expression is a t() function call
|
|
169
|
+
if (value.expression.type === 'CallExpression' &&
|
|
170
|
+
value.expression.callee.type === 'Identifier' &&
|
|
171
|
+
value.expression.callee.name === 't') {
|
|
172
|
+
return; // This is already translated with t()
|
|
173
|
+
}
|
|
174
|
+
// Check if it's a literal string that should be translated
|
|
175
|
+
if (value.expression.type === 'Literal' && typeof value.expression.value === 'string') {
|
|
176
|
+
const text = value.expression.value;
|
|
177
|
+
if (shouldIgnoreText(text))
|
|
178
|
+
return;
|
|
179
|
+
context.report({
|
|
180
|
+
node: value.expression,
|
|
181
|
+
messageId: 'untranslatedAttribute',
|
|
182
|
+
data: {
|
|
183
|
+
attribute: attributeName,
|
|
184
|
+
text,
|
|
185
|
+
},
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
'Literal[value=/\\w/]'(node) {
|
|
191
|
+
if (typeof node.value !== 'string')
|
|
192
|
+
return;
|
|
193
|
+
if (shouldIgnoreText(node.value))
|
|
194
|
+
return;
|
|
195
|
+
if (isInsideTranslateComponent(node))
|
|
196
|
+
return;
|
|
197
|
+
if (isWithinTFunction(node))
|
|
198
|
+
return;
|
|
199
|
+
// Skip if inside JSX attribute (handled by JSXAttribute rule)
|
|
200
|
+
let parent = node.parent;
|
|
201
|
+
while (parent) {
|
|
202
|
+
if (parent.type === 'JSXAttribute')
|
|
203
|
+
return;
|
|
204
|
+
parent = parent.parent;
|
|
205
|
+
}
|
|
206
|
+
// Only report if this appears to be user-facing content
|
|
207
|
+
if (node.parent?.type === 'JSXExpressionContainer' ||
|
|
208
|
+
(node.parent?.type === 'ReturnStatement' && /^[A-Z]/.test(node.value.charAt(0)))) {
|
|
209
|
+
context.report({
|
|
210
|
+
node,
|
|
211
|
+
messageId: 'untranslatedStringLiteral',
|
|
212
|
+
data: {
|
|
213
|
+
text: node.value,
|
|
214
|
+
},
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
TemplateLiteral(node) {
|
|
219
|
+
// Skip if inside translate component or t() function
|
|
220
|
+
if (isInsideTranslateComponent(node))
|
|
221
|
+
return;
|
|
222
|
+
if (isWithinTFunction(node))
|
|
223
|
+
return;
|
|
224
|
+
// Check if this is inside a JSX attribute
|
|
225
|
+
let parent = node.parent;
|
|
226
|
+
while (parent) {
|
|
227
|
+
if (parent.type === 'JSXAttribute') {
|
|
228
|
+
const attr = parent;
|
|
229
|
+
if (attr.name.type === 'JSXIdentifier') {
|
|
230
|
+
const attrName = attr.name.name;
|
|
231
|
+
// Allow template literals in non-user-facing attributes
|
|
232
|
+
// (className, style, key, id, etc. - anything not explicitly user-facing)
|
|
233
|
+
if (!userFacingAttributes.includes(attrName)) {
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
parent = parent.parent;
|
|
239
|
+
}
|
|
240
|
+
// Check if template literal has only static text (no expressions)
|
|
241
|
+
if (node.expressions.length === 0 && node.quasis.length === 1) {
|
|
242
|
+
const text = node.quasis[0].value.cooked || '';
|
|
243
|
+
if (shouldIgnoreText(text))
|
|
244
|
+
return;
|
|
245
|
+
// Only report if in JSX content (JSXExpressionContainer)
|
|
246
|
+
if (node.parent?.type === 'JSXExpressionContainer') {
|
|
247
|
+
context.report({
|
|
248
|
+
node,
|
|
249
|
+
messageId: 'untranslatedTemplateLiteral',
|
|
250
|
+
data: {
|
|
251
|
+
text: text.substring(0, 50), // Limit display length
|
|
252
|
+
},
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
},
|
|
257
|
+
};
|
|
258
|
+
},
|
|
259
|
+
});
|
|
260
|
+
//# sourceMappingURL=require-translation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"require-translation.js","sourceRoot":"","sources":["../../src/rules/require-translation.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@18ways/eslint-plugin-translate",
|
|
3
|
+
"version": "0.0.0-alpha.1aa37fb04f1f",
|
|
4
|
+
"description": "ESLint plugin to detect untranslated strings that should use 18ways translate functionality",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"dev": "tsc --watch",
|
|
13
|
+
"test": "vitest run",
|
|
14
|
+
"test:watch": "vitest watch",
|
|
15
|
+
"lint": "eslint src --ext .ts",
|
|
16
|
+
"prepare": "bun run build"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"eslint",
|
|
20
|
+
"plugin",
|
|
21
|
+
"translation",
|
|
22
|
+
"i18n",
|
|
23
|
+
"18ways",
|
|
24
|
+
"internationalization"
|
|
25
|
+
],
|
|
26
|
+
"author": "18ways",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"peerDependencies": {
|
|
29
|
+
"eslint": "^7.0.0 || ^8.0.0 || ^9.0.0"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/eslint": "^8.56.0",
|
|
33
|
+
"@types/estree": "^1.0.5",
|
|
34
|
+
"@types/node": "^20.0.0",
|
|
35
|
+
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
|
36
|
+
"@typescript-eslint/parser": "^6.0.0",
|
|
37
|
+
"eslint": "^8.56.0",
|
|
38
|
+
"globby": "^11.1.0",
|
|
39
|
+
"typescript": "^5.0.0",
|
|
40
|
+
"vitest": "^3.2.4"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@typescript-eslint/utils": "^6.0.0"
|
|
44
|
+
},
|
|
45
|
+
"engines": {
|
|
46
|
+
"node": ">=16.0.0"
|
|
47
|
+
},
|
|
48
|
+
"packageManager": "bun@1.3.8"
|
|
49
|
+
}
|