mirror of
https://github.com/Stremio/stremio-web.git
synced 2026-03-11 17:15:48 +00:00
feat: added scanner for missed translations
This commit is contained in:
parent
bed2a58060
commit
657f9cd29e
4 changed files with 150 additions and 2 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -3,4 +3,5 @@
|
|||
/yarn.lock
|
||||
/npm-debug.log
|
||||
.DS_Store
|
||||
.prettierignore
|
||||
.prettierignore
|
||||
i18n-report.csv
|
||||
38
package-lock.json
generated
38
package-lock.json
generated
|
|
@ -66,6 +66,7 @@
|
|||
"mini-css-extract-plugin": "2.9.2",
|
||||
"postcss-loader": "8.1.1",
|
||||
"readdirp": "4.0.2",
|
||||
"recast": "0.23.11",
|
||||
"terser-webpack-plugin": "5.3.10",
|
||||
"thread-loader": "^4.0.4",
|
||||
"ts-loader": "^9.5.1",
|
||||
|
|
@ -4786,6 +4787,19 @@
|
|||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/ast-types": {
|
||||
"version": "0.16.1",
|
||||
"resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.16.1.tgz",
|
||||
"integrity": "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/async": {
|
||||
"version": "3.2.6",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
|
||||
|
|
@ -12497,6 +12511,23 @@
|
|||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/recast": {
|
||||
"version": "0.23.11",
|
||||
"resolved": "https://registry.npmjs.org/recast/-/recast-0.23.11.tgz",
|
||||
"integrity": "sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ast-types": "^0.16.1",
|
||||
"esprima": "~4.0.0",
|
||||
"source-map": "~0.6.1",
|
||||
"tiny-invariant": "^1.3.3",
|
||||
"tslib": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 4"
|
||||
}
|
||||
},
|
||||
"node_modules/rechoir": {
|
||||
"version": "0.8.0",
|
||||
"resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz",
|
||||
|
|
@ -13976,6 +14007,13 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tiny-invariant": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
|
||||
"integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tinycolor2": {
|
||||
"version": "1.6.0",
|
||||
"dev": true,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,8 @@
|
|||
"start-prod": "webpack serve --mode production",
|
||||
"build": "webpack --mode production",
|
||||
"test": "jest",
|
||||
"lint": "eslint src"
|
||||
"lint": "eslint src",
|
||||
"scan-translations": "node scan-i18n-report.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.26.0",
|
||||
|
|
@ -70,6 +71,7 @@
|
|||
"mini-css-extract-plugin": "2.9.2",
|
||||
"postcss-loader": "8.1.1",
|
||||
"readdirp": "4.0.2",
|
||||
"recast": "0.23.11",
|
||||
"terser-webpack-plugin": "5.3.10",
|
||||
"thread-loader": "^4.0.4",
|
||||
"ts-loader": "^9.5.1",
|
||||
|
|
|
|||
107
scan-i18n-report.js
Normal file
107
scan-i18n-report.js
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const recast = require('recast');
|
||||
const babelParser = require('@babel/parser');
|
||||
|
||||
const directoryToScan = './src';
|
||||
const report = [];
|
||||
|
||||
function toKey(str) {
|
||||
return str
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9\s]/g, '')
|
||||
.replace(/\s+/g, '_')
|
||||
.slice(0, 40);
|
||||
}
|
||||
|
||||
function scanFile(filePath) {
|
||||
try {
|
||||
const code = fs.readFileSync(filePath, 'utf8');
|
||||
const ast = babelParser.parse(code, {
|
||||
sourceType: 'module',
|
||||
plugins: [
|
||||
'jsx',
|
||||
'typescript',
|
||||
'classProperties',
|
||||
'objectRestSpread',
|
||||
'optionalChaining',
|
||||
'nullishCoalescingOperator',
|
||||
],
|
||||
errorRecovery: true,
|
||||
});
|
||||
|
||||
recast.types.visit(ast, {
|
||||
// Text directly inside JSX
|
||||
visitJSXText(path) {
|
||||
const text = path.node.value.trim();
|
||||
if (text.length > 1 && /\w/.test(text)) {
|
||||
const loc = path.node.loc?.start || { line: 0 };
|
||||
report.push({
|
||||
file: filePath,
|
||||
line: loc.line,
|
||||
string: text,
|
||||
key: toKey(text),
|
||||
});
|
||||
}
|
||||
this.traverse(path);
|
||||
},
|
||||
|
||||
// { "hello" } style
|
||||
visitJSXExpressionContainer(path) {
|
||||
const expr = path.node.expression;
|
||||
|
||||
// Skip expressions that call t()
|
||||
if (
|
||||
expr.type === 'CallExpression' &&
|
||||
expr.callee.type === 'Identifier' &&
|
||||
expr.callee.name === 't'
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find only { "text" } expressions
|
||||
if (expr.type === 'StringLiteral') {
|
||||
const parent = path.parentPath.node;
|
||||
if (parent.type === 'JSXElement') {
|
||||
const loc = path.node.loc?.start || { line: 0 };
|
||||
report.push({
|
||||
file: filePath,
|
||||
line: loc.line,
|
||||
string: expr.value,
|
||||
key: toKey(expr.value),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.traverse(path);
|
||||
}
|
||||
});
|
||||
|
||||
} catch (err) {
|
||||
console.warn(`❌ Skipping ${filePath}: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
function walk(dir) {
|
||||
fs.readdirSync(dir).forEach((file) => {
|
||||
const fullPath = path.join(dir, file);
|
||||
if (fs.statSync(fullPath).isDirectory()) {
|
||||
walk(fullPath);
|
||||
} else if (/\.(js|jsx|ts|tsx)$/.test(file)) {
|
||||
console.log('Scanning file:', fullPath);
|
||||
scanFile(fullPath);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function writeReport() {
|
||||
const header = `File,Line,Hardcoded String,Suggested Key\n`;
|
||||
const rows = report.map((row) =>
|
||||
`"${row.file}",${row.line},"${row.string.replace(/"/g, '""')}","${row.key}"`
|
||||
);
|
||||
fs.writeFileSync('i18n-report.csv', header + rows.join('\n'));
|
||||
console.log('✅ Report written to i18n-report.csv');
|
||||
}
|
||||
|
||||
walk(directoryToScan);
|
||||
writeReport();
|
||||
Loading…
Reference in a new issue