i18ntk 4.5.4 → 4.6.1

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.
@@ -2,8 +2,12 @@
2
2
 
3
3
  const fs = require('fs');
4
4
  const path = require('path');
5
+ const { getSourceExtensions, getExcludeDirs } = require('./framework-detector');
5
6
  const SecurityUtils = require('./security');
6
7
 
8
+ const EXCLUDE_DIRS = new Set(getExcludeDirs());
9
+ const SOURCE_EXTENSIONS = getSourceExtensions();
10
+
7
11
  const ISSUE_TYPES = new Set([
8
12
  'missing_key',
9
13
  'unused_key',
@@ -265,7 +269,7 @@ function discoverLocaleFiles(localesDir) {
265
269
  function walk(dir, extensions, acc = [], basePath = process.cwd()) {
266
270
  if (!SecurityUtils.safeExistsSync(dir, basePath)) return acc;
267
271
  for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
268
- if (['node_modules', '.git', 'dist', 'build', 'coverage', 'out', 'tmp', 'temp', 'test', 'tests', 'test_env', 'i18ntk-reports'].includes(entry.name)) continue;
272
+ if (EXCLUDE_DIRS.has(entry.name)) continue;
269
273
  const entryPath = path.join(dir, entry.name);
270
274
  if (entry.isDirectory()) walk(entryPath, extensions, acc, basePath);
271
275
  else if (entry.isFile() && extensions.includes(path.extname(entry.name))) acc.push(entryPath);
@@ -290,7 +294,7 @@ function scanSourceFiles(sourceDir, availableKeys) {
290
294
  const hardcodedTexts = [];
291
295
  const translationValueIndex = new Map();
292
296
  const basePath = path.resolve(sourceDir);
293
- const files = walk(sourceDir, ['.js', '.jsx', '.ts', '.tsx', '.vue', '.svelte'], [], basePath);
297
+ const files = walk(sourceDir, SOURCE_EXTENSIONS, [], basePath);
294
298
 
295
299
  for (const file of files) {
296
300
  const content = SecurityUtils.safeReadFileSync(file, basePath, 'utf8');
@@ -339,7 +343,13 @@ function findKeyByValue(_text, _availableKeys) {
339
343
  function extractPlaceholders(value) {
340
344
  const placeholders = new Set();
341
345
  const text = String(value || '');
342
- const patterns = [/\{\{\s*([\w.-]+)\s*\}\}/g, /%\{([\w.-]+)\}/g, /\{([\w.-]+)\}/g];
346
+ const patterns = [
347
+ /\{\{\s*([\w.-]+)\s*\}\}/g,
348
+ /%\{([\w.-]+)\}/g,
349
+ /\{([\w.-]+)\}/g,
350
+ /\$\{([\w.-]+)\}/g,
351
+ /\$([a-zA-Z_][a-zA-Z0-9_-]+)/g
352
+ ];
343
353
  for (const pattern of patterns) {
344
354
  let match;
345
355
  while ((match = pattern.exec(text))) placeholders.add(match[1]);
@@ -356,7 +366,10 @@ function looksLikelyUntranslated(source, target, locale) {
356
366
  }
357
367
 
358
368
  function normalizeText(value) {
359
- return String(value || '').replace(/\{\{[^}]+\}\}|\{[^}]+\}|%\{[^}]+\}/g, '').trim().toLowerCase();
369
+ return String(value || '')
370
+ .replace(/\{\{[^}]+\}\}|\{[^}]+\}|%\{[^}]+\}/g, '')
371
+ .replace(/\$[a-zA-Z_][a-zA-Z0-9_-]*(?:\s*\{\s*[^}]+\})?/g, '')
372
+ .trim().toLowerCase();
360
373
  }
361
374
 
362
375
  function getExpansionPercent(source, target) {
@@ -2,8 +2,9 @@
2
2
 
3
3
  const path = require('path');
4
4
  const SecurityUtils = require('./security');
5
+ const { SOURCE_DIRS } = require('./framework-detector');
5
6
 
6
- const DEFAULT_SOURCE_DIRS = ['src', 'app', 'lib', 'source'];
7
+ const DEFAULT_SOURCE_DIRS = SOURCE_DIRS;
7
8
 
8
9
  function resolveUsageSourceDir(options = {}) {
9
10
  const projectRoot = path.resolve(options.projectRoot || process.cwd());
@@ -34,24 +34,32 @@ const ENGLISH_WORDS = new Set([
34
34
  'warning', 'when', 'with', 'without'
35
35
  ]);
36
36
 
37
- const DEFAULT_ALLOWED_ENGLISH_TERMS = new Set([
38
- 'api',
39
- ]);
37
+ const DEFAULT_ALLOWED_ENGLISH_TERMS = new Set(['api']);
38
+
39
+ function resolveAllowedEnglishTerms(options, projectConfig) {
40
+ const terms = new Set(DEFAULT_ALLOWED_ENGLISH_TERMS);
41
+ const sources = [
42
+ options && options.allowedEnglishTerms,
43
+ projectConfig && projectConfig.allowedEnglishTerms
44
+ ];
45
+ for (const source of sources) {
46
+ if (Array.isArray(source)) {
47
+ source.forEach(term => {
48
+ if (typeof term === 'string' && term.trim()) {
49
+ terms.add(term.trim().toLowerCase());
50
+ }
51
+ });
52
+ }
53
+ }
54
+ return terms;
55
+ }
40
56
 
41
57
  function normalizeLanguage(language) {
42
58
  return String(language || '').toLowerCase().split(/[-_]/)[0];
43
59
  }
44
60
 
45
61
  function toAllowedTermSet(terms) {
46
- const allowed = new Set(DEFAULT_ALLOWED_ENGLISH_TERMS);
47
- if (Array.isArray(terms)) {
48
- terms.forEach(term => {
49
- if (typeof term === 'string' && term.trim()) {
50
- allowed.add(term.trim().toLowerCase());
51
- }
52
- });
53
- }
54
- return allowed;
62
+ return resolveAllowedEnglishTerms({ allowedEnglishTerms: terms });
55
63
  }
56
64
 
57
65
  function stripNonLanguageTokens(value) {
@@ -171,5 +179,6 @@ module.exports = {
171
179
  DEFAULT_ENGLISH_THRESHOLD_PERCENT,
172
180
  analyzeEnglishContent,
173
181
  detectTranslationContentRisks,
174
- hasSecretLikeValue
182
+ hasSecretLikeValue,
183
+ resolveAllowedEnglishTerms
175
184
  };