i18n-smell-detector 0.3.0 → 0.4.0

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 CHANGED
@@ -28,6 +28,7 @@ npm install -D i18n-smell-detector
28
28
  Run the check:
29
29
 
30
30
  ```bash
31
+ npx i18n-smell-detector check --config i18n-smell.config.mjs
31
32
  npx i18n-smell-detector check-identical --config i18n-smell.config.mjs
32
33
  npx i18n-smell-detector check-hardcoded --config i18n-smell.config.mjs
33
34
  ```
@@ -39,20 +40,82 @@ npx i18n-smell-detector check-identical --format console
39
40
  npx i18n-smell-detector check-identical --format json
40
41
  npx i18n-smell-detector check-identical --format markdown
41
42
  npx i18n-smell-detector check-hardcoded --format json
43
+ npx i18n-smell-detector check --format markdown --output i18n-report.md
42
44
  ```
43
45
 
46
+ CLI options:
47
+
48
+ | Option | Description |
49
+ |---|---|
50
+ | `-c, --config <path>` | Use a specific config file. |
51
+ | `--format <format>` | Print `console`, `json`, or `markdown` output. |
52
+ | `--fail-on <level>` | Exit with code `1` for findings at `high`, `medium`, or `low`. Use `none` while tuning. |
53
+ | `--include-ignored` | Include ignored findings in the report. |
54
+ | `--output <path>` | Write the full report to a file and print a short summary. |
55
+ | `--baseline <path>` | Suppress findings stored in a baseline file. |
56
+ | `--update-baseline` | Write the current findings to the baseline file. |
57
+ | `-h, --help` | Show CLI help. |
58
+
44
59
  ## CI usage
45
60
 
46
61
  Use `--fail-on` to choose the lowest severity that should make the command exit with code `1`.
47
62
 
48
63
  ```bash
49
- npx i18n-smell-detector check-identical --config i18n-smell.config.mjs --fail-on high
64
+ npx i18n-smell-detector check --config i18n-smell.config.mjs --fail-on high
50
65
  ```
51
66
 
52
67
  For a new project, it is often easier to start in reporting mode while tuning allowlists:
53
68
 
54
69
  ```bash
55
- npx i18n-smell-detector check-identical --config i18n-smell.config.mjs --fail-on none
70
+ npx i18n-smell-detector check --config i18n-smell.config.mjs --fail-on none
71
+ ```
72
+
73
+ ### GitHub Actions example
74
+
75
+ Add a workflow such as `.github/workflows/i18n-smell.yml`:
76
+
77
+ ```yaml
78
+ name: i18n smell check
79
+
80
+ on:
81
+ pull_request:
82
+ push:
83
+ branches: [main]
84
+
85
+ jobs:
86
+ i18n-smell:
87
+ runs-on: ubuntu-latest
88
+
89
+ steps:
90
+ - uses: actions/checkout@v6
91
+
92
+ - uses: actions/setup-node@v6
93
+ with:
94
+ node-version: 22
95
+
96
+ - run: npm ci
97
+
98
+ - name: Run i18n checks
99
+ run: npx i18n-smell-detector check --config i18n-smell.config.mjs --fail-on high
100
+
101
+ - name: Write Markdown report
102
+ if: always()
103
+ run: npx i18n-smell-detector check --config i18n-smell.config.mjs --format markdown --output reports/i18n-smell.md --fail-on none
104
+
105
+ - uses: actions/upload-artifact@v4
106
+ if: always()
107
+ with:
108
+ name: i18n-smell-report
109
+ path: reports/i18n-smell.md
110
+ ```
111
+
112
+ For this repository, the local smoke commands are:
113
+
114
+ ```bash
115
+ npm test
116
+ npm run check
117
+ npm run example:basic
118
+ node bin/i18n-smell-detector.js --help
56
119
  ```
57
120
 
58
121
  ## Configuration
@@ -62,6 +125,10 @@ Create `i18n-smell.config.mjs`:
62
125
  ```js
63
126
  export default {
64
127
  baseLocale: 'en',
128
+ checks: {
129
+ identical: true,
130
+ hardcoded: true
131
+ },
65
132
  source: [
66
133
  'src/**/*.vue'
67
134
  ],
@@ -117,6 +184,8 @@ export default {
117
184
  trimWhitespace: true,
118
185
  ignoreCase: false,
119
186
  includeIgnored: false,
187
+ format: 'console',
188
+ baseline: '.i18n-smell-baseline.json',
120
189
  failOn: 'high'
121
190
  };
122
191
  ```
@@ -126,6 +195,7 @@ export default {
126
195
  | Option | Type | Default | Description |
127
196
  |---|---|---:|---|
128
197
  | `baseLocale` | `string` | `'en'` | Locale used as the comparison source. It must also be listed in `locales`. |
198
+ | `checks` | `{ identical?: boolean, hardcoded?: boolean }` | both enabled | Checks run by the combined `check` command. Existing single-check commands ignore this setting. |
129
199
  | `source` | `string[]` | `['src/**/*.vue']` | Source globs used by `check-hardcoded`. `node_modules` is ignored. |
130
200
  | `locales` | `Record<string, string>` | `{}` | Map of locale codes to JSON locale file paths. Relative paths are resolved from the config file directory. |
131
201
  | `allowIdenticalKeys` | `(string \| RegExp)[]` | `[]` | Key rules that are allowed to match the base locale. String rules support `*` wildcards, for example `brand.*`. `RegExp` rules are tested against the flattened key. |
@@ -140,6 +210,9 @@ export default {
140
210
  | `ignoreCase` | `boolean` | `false` | Compare values case-insensitively when set to `true`. |
141
211
  | `ignoreSameLanguageFamily` | `boolean` | `true` | Ignore regional variants that share the same language family, such as `en` and `en-GB`. Set to `false` to check regional variants too. |
142
212
  | `includeIgnored` | `boolean` | `false` | Include ignored findings in reports. Can also be enabled with `--include-ignored`. |
213
+ | `format` | `'console' \| 'json' \| 'markdown'` | `'console'` | Default report format. CLI `--format` takes precedence. |
214
+ | `output` | `string` | unset | Report file path. CLI `--output` takes precedence. |
215
+ | `baseline` | `string` | unset | Baseline file path used by `--baseline` or `--update-baseline`. |
143
216
 
144
217
  ## Example output
145
218
 
@@ -241,6 +314,69 @@ JSON output includes source locations:
241
314
  }
242
315
  ```
243
316
 
317
+ ## Running All Checks
318
+
319
+ Use `check` to run every enabled check in one command:
320
+
321
+ ```bash
322
+ npx i18n-smell-detector check --config i18n-smell.config.mjs
323
+ ```
324
+
325
+ Disable checks during gradual rollout:
326
+
327
+ ```js
328
+ export default {
329
+ checks: {
330
+ identical: true,
331
+ hardcoded: false
332
+ }
333
+ };
334
+ ```
335
+
336
+ Combined JSON output groups counts by check:
337
+
338
+ ```json
339
+ {
340
+ "summary": {
341
+ "identical": {
342
+ "high": 2,
343
+ "medium": 1,
344
+ "low": 0,
345
+ "ignored": 16
346
+ },
347
+ "hardcoded": {
348
+ "high": 3,
349
+ "medium": 2,
350
+ "low": 0,
351
+ "ignored": 3
352
+ }
353
+ },
354
+ "issues": []
355
+ }
356
+ ```
357
+
358
+ ## Writing Reports
359
+
360
+ Use `--output` to write the full report to a file. The CLI prints a short summary to stdout after writing.
361
+
362
+ ```bash
363
+ npx i18n-smell-detector check --format markdown --output reports/i18n-report.md
364
+ npx i18n-smell-detector check --format json --output reports/i18n-report.json
365
+ ```
366
+
367
+ Markdown output for `check` includes a summary table and one section per check, which makes it suitable for CI artifacts.
368
+
369
+ ## Gradual Adoption
370
+
371
+ Use a baseline to ignore existing findings while still failing CI for new ones:
372
+
373
+ ```bash
374
+ npx i18n-smell-detector check --baseline .i18n-smell-baseline.json --update-baseline
375
+ npx i18n-smell-detector check --baseline .i18n-smell-baseline.json
376
+ ```
377
+
378
+ The baseline stores stable issue identifiers. Updating it removes findings that no longer exist and records the current visible findings.
379
+
244
380
  ## Exit codes
245
381
 
246
382
  | Code | Meaning |
@@ -326,6 +462,7 @@ Useful tuning patterns:
326
462
  npm install
327
463
  npm test
328
464
  npm run check
465
+ npm run example:basic
329
466
  ```
330
467
 
331
468
  ## License
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "i18n-smell-detector",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Find locale values copied from the base locale.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -21,9 +21,10 @@
21
21
  "LICENSE"
22
22
  ],
23
23
  "scripts": {
24
- "check": "npm run check:identical && npm run check:hardcoded",
24
+ "check": "node ./bin/i18n-smell-detector.js check --config ./examples/vue-vite/i18n-smell.config.mjs --fail-on none",
25
25
  "check:identical": "node ./bin/i18n-smell-detector.js check-identical --config ./examples/vue-vite/i18n-smell.config.mjs --fail-on none",
26
26
  "check:hardcoded": "node ./bin/i18n-smell-detector.js check-hardcoded --config ./examples/vue-vite/i18n-smell.config.mjs --fail-on none",
27
+ "example:basic": "node ./bin/i18n-smell-detector.js check --config ./examples/basic/i18n-smell.config.mjs --fail-on none",
27
28
  "test": "node --test"
28
29
  },
29
30
  "keywords": [
@@ -0,0 +1,75 @@
1
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+
4
+ export function issueId(check, issue) {
5
+ if (check === 'identical') return `identical:${issue.targetLocale}:${issue.key}`;
6
+ return `hardcoded:${issue.file}:${issue.line}:${issue.column}:${issue.value}`;
7
+ }
8
+
9
+ export async function loadBaseline(filePath) {
10
+ if (!filePath) return new Set();
11
+
12
+ let parsed;
13
+ try {
14
+ parsed = JSON.parse(await readFile(filePath, 'utf8'));
15
+ } catch (error) {
16
+ if (error && typeof error === 'object' && error.code === 'ENOENT') return new Set();
17
+ throw new Error(`Failed to read baseline file: ${filePath}\nReason: ${error.message}`);
18
+ }
19
+
20
+ if (!parsed || !Array.isArray(parsed.issues)) {
21
+ throw new Error(`Invalid baseline file: ${filePath}`);
22
+ }
23
+
24
+ return new Set(parsed.issues.map((issue) => issue.id).filter(Boolean));
25
+ }
26
+
27
+ export function applyBaseline(results, ids) {
28
+ if (!ids.size) return results;
29
+
30
+ return results.map((result) => ({
31
+ ...result,
32
+ issues: result.issues.map((issue) => {
33
+ const id = issueId(result.check, issue);
34
+ if (!ids.has(id)) return { ...issue, id };
35
+ return { ...issue, id, severity: 'ignored', reason: 'baseline' };
36
+ }),
37
+ }));
38
+ }
39
+
40
+ export async function writeBaseline(filePath, results) {
41
+ const issues = [];
42
+
43
+ for (const result of results) {
44
+ for (const issue of result.issues) {
45
+ if (issue.severity === 'ignored') continue;
46
+ issues.push(toBaselineIssue(result.check, issue));
47
+ }
48
+ }
49
+
50
+ await mkdir(path.dirname(filePath), { recursive: true });
51
+ await writeFile(filePath, `${JSON.stringify({ version: 1, issues }, null, 2)}\n`);
52
+ return issues.length;
53
+ }
54
+
55
+ function toBaselineIssue(check, issue) {
56
+ const base = {
57
+ id: issueId(check, issue),
58
+ value: issue.value,
59
+ };
60
+
61
+ if (check === 'identical') {
62
+ return {
63
+ ...base,
64
+ key: issue.key,
65
+ targetLocale: issue.targetLocale,
66
+ };
67
+ }
68
+
69
+ return {
70
+ ...base,
71
+ file: issue.file,
72
+ line: issue.line,
73
+ column: issue.column,
74
+ };
75
+ }
package/src/cli.js CHANGED
@@ -1,9 +1,10 @@
1
+ import { mkdir, writeFile } from 'node:fs/promises';
1
2
  import path from 'node:path';
2
- import { checkHardcodedStrings } from './check-hardcoded.js';
3
- import { checkIdenticalTranslations } from './check-identical.js';
3
+ import { applyBaseline, loadBaseline, writeBaseline } from './baseline.js';
4
4
  import { loadConfig, resolveConfigPath } from './config.js';
5
- import { loadFlattenedLocales } from './locale/load-locales.js';
5
+ import { flattenResults, renderCombinedReport, summarizeResults } from './reporters/combined.js';
6
6
  import { renderReport } from './reporters/index.js';
7
+ import { enabledChecks, runChecks } from './run-checks.js';
7
8
  import { failRank, severityRank } from './severity.js';
8
9
 
9
10
  const HELP = `
@@ -18,6 +19,9 @@ Options:
18
19
  -c, --config <path> Config file path
19
20
  --format <format> console | json | markdown (default: console)
20
21
  --fail-on <level> high | medium | low | none (default: config.failOn or high)
22
+ --output <path> Write the full report to a file
23
+ --baseline <path> Read a baseline file
24
+ --update-baseline Write the current issues to the baseline file
21
25
  --include-ignored Print ignored matches as well
22
26
  -h, --help Show help
23
27
 
@@ -29,7 +33,7 @@ Example:
29
33
  function parseArgs(argv) {
30
34
  const args = argv.slice(2);
31
35
  const command = args[0] && !args[0].startsWith('-') ? args.shift() : 'check-identical';
32
- const options = { command, format: 'console', includeIgnored: false, help: false };
36
+ const options = { command, includeIgnored: false, help: false, updateBaseline: false };
33
37
 
34
38
  for (let index = 0; index < args.length; index += 1) {
35
39
  const arg = args[index];
@@ -42,6 +46,12 @@ function parseArgs(argv) {
42
46
  options.format = readChoice(args[++index], ['console', 'json', 'markdown'], 'format');
43
47
  } else if (arg === '--fail-on') {
44
48
  options.failOn = readChoice(args[++index], ['none', 'low', 'medium', 'high'], 'fail-on level');
49
+ } else if (arg === '--output') {
50
+ options.output = args[++index];
51
+ } else if (arg === '--baseline') {
52
+ options.baseline = args[++index];
53
+ } else if (arg === '--update-baseline') {
54
+ options.updateBaseline = true;
45
55
  } else if (arg === '--include-ignored') {
46
56
  options.includeIgnored = true;
47
57
  } else {
@@ -76,24 +86,88 @@ export async function runCli(argv) {
76
86
  const effectiveConfig = {
77
87
  ...config,
78
88
  failOn: options.failOn || config.failOn || 'high',
89
+ format: options.format || config.format || 'console',
90
+ output: options.output || config.output,
91
+ baseline: options.baseline || config.baseline,
79
92
  includeIgnored: options.includeIgnored || config.includeIgnored || false,
80
93
  };
81
94
 
82
95
  const configDir = path.dirname(configPath);
83
- const isHardcoded = options.command === 'check-hardcoded';
84
- const issues = isHardcoded
85
- ? await checkHardcodedStrings(effectiveConfig, configDir)
86
- : checkIdenticalTranslations(await loadFlattenedLocales(effectiveConfig, configDir), effectiveConfig);
96
+ const checks = commandChecks(options.command, effectiveConfig);
97
+ const rawResults = await runChecks(effectiveConfig, configDir, checks);
98
+
99
+ if (options.updateBaseline) {
100
+ if (!effectiveConfig.baseline) throw new Error('Baseline path is required for --update-baseline');
101
+ const baselinePath = resolveFromConfigDir(effectiveConfig.baseline, configDir);
102
+ const count = await writeBaseline(baselinePath, rawResults);
103
+ console.log(`Baseline updated: ${effectiveConfig.baseline} (${count} issues)`);
104
+ return;
105
+ }
87
106
 
88
- console.log(renderReport(issues, {
89
- format: options.format,
107
+ const baselineIds = await loadBaseline(resolveOptional(effectiveConfig.baseline, configDir));
108
+ const results = applyBaseline(rawResults, baselineIds);
109
+ const reportOptions = {
110
+ format: effectiveConfig.format,
90
111
  includeIgnored: effectiveConfig.includeIgnored || false,
91
- title: isHardcoded ? 'hardcoded strings' : 'identical translations',
92
- heading: isHardcoded ? 'Hardcoded strings' : 'Identical translations',
93
- emptyMessage: isHardcoded ? 'No hardcoded strings found.' : 'No copied base-locale values found.',
94
- }));
112
+ };
113
+ const report = options.command === 'check'
114
+ ? renderCombinedReport(results, reportOptions)
115
+ : renderSingleReport(results[0], reportOptions);
116
+
117
+ if (effectiveConfig.output) {
118
+ await writeOutput(resolveFromConfigDir(effectiveConfig.output, configDir), report);
119
+ console.log(renderWriteSummary(results, effectiveConfig.output));
120
+ } else {
121
+ console.log(report);
122
+ }
95
123
 
96
- const visible = effectiveConfig.includeIgnored ? issues : issues.filter((issue) => issue.severity !== 'ignored');
124
+ const visible = options.command === 'check'
125
+ ? flattenResults(results, { includeIgnored: effectiveConfig.includeIgnored || false })
126
+ : visibleIssues(results[0].issues, effectiveConfig.includeIgnored || false);
97
127
  const shouldFail = visible.some((issue) => severityRank[issue.severity] >= failRank(effectiveConfig.failOn || 'high'));
98
128
  if (shouldFail) process.exit(1);
99
129
  }
130
+
131
+ function commandChecks(command, config) {
132
+ if (command === 'check-identical') return ['identical'];
133
+ if (command === 'check-hardcoded') return ['hardcoded'];
134
+ return enabledChecks(config);
135
+ }
136
+
137
+ function renderSingleReport(result, options) {
138
+ return renderReport(result.issues, {
139
+ ...options,
140
+ title: result.title,
141
+ heading: result.heading,
142
+ emptyMessage: result.emptyMessage,
143
+ });
144
+ }
145
+
146
+ function visibleIssues(issues, includeIgnored) {
147
+ return includeIgnored ? issues : issues.filter((issue) => issue.severity !== 'ignored');
148
+ }
149
+
150
+ function resolveFromConfigDir(filePath, configDir) {
151
+ return path.isAbsolute(filePath) ? filePath : path.resolve(configDir, filePath);
152
+ }
153
+
154
+ function resolveOptional(filePath, configDir) {
155
+ return filePath ? resolveFromConfigDir(filePath, configDir) : undefined;
156
+ }
157
+
158
+ async function writeOutput(filePath, report) {
159
+ try {
160
+ await mkdir(path.dirname(filePath), { recursive: true });
161
+ await writeFile(filePath, report);
162
+ } catch (error) {
163
+ throw new Error(`Failed to write report file: ${filePath}\nReason: ${error.message}`);
164
+ }
165
+ }
166
+
167
+ function renderWriteSummary(results, output) {
168
+ const summary = summarizeResults(results);
169
+ const counts = Object.entries(summary)
170
+ .map(([check, item]) => `${check}: high=${item.high} medium=${item.medium} low=${item.low} ignored=${item.ignored}`)
171
+ .join('; ');
172
+ return `Report written to ${output}\n${counts}`;
173
+ }
package/src/config.js CHANGED
@@ -98,6 +98,22 @@ function validateConfig(config) {
98
98
  throw new Error('Config hardcoded must be an object');
99
99
  }
100
100
 
101
+ if ('checks' in candidate && (!candidate.checks || typeof candidate.checks !== 'object' || Array.isArray(candidate.checks))) {
102
+ throw new Error('Config checks must be an object');
103
+ }
104
+
105
+ if ('format' in candidate && !['console', 'json', 'markdown'].includes(candidate.format)) {
106
+ throw new Error('Config format must be console, json, or markdown');
107
+ }
108
+
109
+ if ('output' in candidate && typeof candidate.output !== 'string') {
110
+ throw new Error('Config output must be a file path string');
111
+ }
112
+
113
+ if ('baseline' in candidate && typeof candidate.baseline !== 'string') {
114
+ throw new Error('Config baseline must be a file path string');
115
+ }
116
+
101
117
  const hardcoded = /** @type {Record<string, unknown>} */ (candidate.hardcoded || {});
102
118
  if ('vueAttributes' in hardcoded && !isStringArray(hardcoded.vueAttributes)) {
103
119
  throw new Error('Config hardcoded.vueAttributes must be an array of strings');
@@ -118,7 +134,17 @@ function withDefaults(config) {
118
134
  ignoreCase: false,
119
135
  includeIgnored: false,
120
136
  failOn: 'high',
137
+ checks: {
138
+ identical: true,
139
+ hardcoded: true,
140
+ ...(config.checks || {}),
141
+ },
121
142
  ...config,
143
+ checks: {
144
+ identical: true,
145
+ hardcoded: true,
146
+ ...(config.checks || {}),
147
+ },
122
148
  source: config.source || ['src/**/*.vue'],
123
149
  hardcoded: normalizeHardcodedConfig(config.hardcoded || {}),
124
150
  placeholderPatterns: normalizePlaceholderPatterns(config.placeholderPatterns || DEFAULT_PLACEHOLDER_PATTERNS),
@@ -0,0 +1,66 @@
1
+ import { renderConsoleReport } from './console.js';
2
+ import { renderMarkdownReport } from './markdown.js';
3
+ import { summarizeIssues } from './summary.js';
4
+
5
+ export function renderCombinedReport(results, options) {
6
+ if (options.format === 'json') return renderCombinedJson(results, options);
7
+ if (options.format === 'markdown') return renderCombinedMarkdown(results, options);
8
+ return renderCombinedConsole(results, options);
9
+ }
10
+
11
+ export function flattenResults(results, options) {
12
+ return results.flatMap((result) => {
13
+ const issues = options.includeIgnored ? result.issues : result.issues.filter((issue) => issue.severity !== 'ignored');
14
+ return issues.map((issue) => ({ check: result.check, ...issue }));
15
+ });
16
+ }
17
+
18
+ export function summarizeResults(results) {
19
+ return Object.fromEntries(results.map((result) => [result.check, summarizeIssues(result.issues)]));
20
+ }
21
+
22
+ function renderCombinedJson(results, options) {
23
+ return `${JSON.stringify({
24
+ summary: summarizeResults(results),
25
+ issues: flattenResults(results, options),
26
+ }, null, 2)}\n`;
27
+ }
28
+
29
+ function renderCombinedConsole(results, options) {
30
+ return results.map((result) => renderConsoleReport(result.issues, {
31
+ ...options,
32
+ title: result.title,
33
+ emptyMessage: result.emptyMessage,
34
+ })).join('\n\n');
35
+ }
36
+
37
+ function renderCombinedMarkdown(results, options) {
38
+ const lines = [
39
+ '# i18n-smell-detector report',
40
+ '',
41
+ '## Summary',
42
+ '',
43
+ '| Check | High | Medium | Low | Ignored |',
44
+ '|---|---:|---:|---:|---:|',
45
+ ];
46
+
47
+ for (const result of results) {
48
+ const summary = summarizeIssues(result.issues);
49
+ lines.push(`| ${result.check} | ${summary.high} | ${summary.medium} | ${summary.low} | ${summary.ignored} |`);
50
+ }
51
+
52
+ for (const result of results) {
53
+ lines.push('');
54
+ lines.push(asSection(renderMarkdownReport(result.issues, {
55
+ ...options,
56
+ heading: result.heading,
57
+ emptyMessage: result.emptyMessage,
58
+ }).trim()));
59
+ }
60
+
61
+ return `${lines.join('\n')}\n`;
62
+ }
63
+
64
+ function asSection(markdown) {
65
+ return markdown.replace(/^# /, '## ');
66
+ }
@@ -0,0 +1,35 @@
1
+ import { checkHardcodedStrings } from './check-hardcoded.js';
2
+ import { checkIdenticalTranslations } from './check-identical.js';
3
+ import { loadFlattenedLocales } from './locale/load-locales.js';
4
+
5
+ const CHECK_META = {
6
+ identical: {
7
+ title: 'identical translations',
8
+ heading: 'Identical translations',
9
+ emptyMessage: 'No copied base-locale values found.',
10
+ },
11
+ hardcoded: {
12
+ title: 'hardcoded strings',
13
+ heading: 'Hardcoded strings',
14
+ emptyMessage: 'No hardcoded strings found.',
15
+ },
16
+ };
17
+
18
+ export async function runChecks(config, configDir, names) {
19
+ const results = [];
20
+
21
+ for (const check of names) {
22
+ if (check === 'identical') {
23
+ const locales = await loadFlattenedLocales(config, configDir);
24
+ results.push({ check, ...CHECK_META[check], issues: checkIdenticalTranslations(locales, config) });
25
+ } else if (check === 'hardcoded') {
26
+ results.push({ check, ...CHECK_META[check], issues: await checkHardcodedStrings(config, configDir) });
27
+ }
28
+ }
29
+
30
+ return results;
31
+ }
32
+
33
+ export function enabledChecks(config) {
34
+ return ['identical', 'hardcoded'].filter((check) => config.checks[check]);
35
+ }
package/src/types.js CHANGED
@@ -10,6 +10,10 @@
10
10
  * @property {(string | RegExp)[]=} placeholderPatterns
11
11
  * @property {string[]=} source
12
12
  * @property {{vueAttributes: string[], ignoreValues: (string | RegExp)[], ignorePatterns: RegExp[]}=} hardcoded
13
+ * @property {{identical: boolean, hardcoded: boolean}=} checks
14
+ * @property {ReportFormat=} format
15
+ * @property {string=} output
16
+ * @property {string=} baseline
13
17
  * @property {boolean=} ignoreCodeLike
14
18
  * @property {boolean=} ignoreSameLanguageFamily
15
19
  * @property {boolean=} trimWhitespace