mirror of
https://github.com/itdoginfo/podkop.git
synced 2025-12-06 03:26:51 +03:00
feat: translate some keys
This commit is contained in:
@@ -1,93 +1,75 @@
|
||||
import fg from 'fast-glob';
|
||||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
import glob from 'fast-glob';
|
||||
import { parse } from '@babel/parser';
|
||||
import traverse from '@babel/traverse';
|
||||
import * as t from '@babel/types';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { dirname } from 'path';
|
||||
|
||||
const outputFile = 'locales/calls.json';
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
const tsSearchGlob = 'src/**/*.ts';
|
||||
const jsSearchGlob = '../luci-app-podkop/htdocs/luci-static/resources/view/podkop/**/*.js';
|
||||
|
||||
function extractAllUnderscoreCallsFromContent(content) {
|
||||
const results = [];
|
||||
let index = 0;
|
||||
|
||||
while (index < content.length) {
|
||||
const start = content.indexOf('_(', index);
|
||||
if (start === -1) break;
|
||||
|
||||
let i = start + 2;
|
||||
let depth = 1;
|
||||
|
||||
while (i < content.length && depth > 0) {
|
||||
if (content[i] === '(') depth++;
|
||||
else if (content[i] === ')') depth--;
|
||||
i++;
|
||||
}
|
||||
|
||||
const raw = content.slice(start, i);
|
||||
results.push({ raw, index: start });
|
||||
index = i;
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
function getLineNumber(content, charIndex) {
|
||||
return content.slice(0, charIndex).split('\n').length;
|
||||
}
|
||||
|
||||
function extractKey(call) {
|
||||
const match = call.match(/^_\(\s*(['"`])((?:\\\1|.)*?)\1\s*\)$/);
|
||||
return match ? match[2].trim() : null;
|
||||
}
|
||||
|
||||
function normalizeCall(call) {
|
||||
return call
|
||||
.replace(/\s*\n\s*/g, ' ')
|
||||
.replace(/\s+/g, ' ')
|
||||
.replace(/\(\s+/g, '(')
|
||||
.replace(/\s+\)/g, ')')
|
||||
.replace(/,\s*\)$/, ')')
|
||||
.trim();
|
||||
}
|
||||
|
||||
async function extractAllUnderscoreCallsWithLocations() {
|
||||
const files = [
|
||||
...(await fg(tsSearchGlob, { ignore: ['**/*test.ts'], absolute: true })),
|
||||
...(await fg(jsSearchGlob, { ignore: ['**/main.js'], absolute: true })),
|
||||
];
|
||||
|
||||
const callMap = new Map();
|
||||
|
||||
for (const file of files) {
|
||||
const content = await fs.readFile(file, 'utf8');
|
||||
const relativePath = path.relative(process.cwd(), file);
|
||||
const extracted = extractAllUnderscoreCallsFromContent(content);
|
||||
|
||||
for (const { raw, index } of extracted) {
|
||||
const line = getLineNumber(content, index);
|
||||
const location = `${relativePath}:${line}`;
|
||||
|
||||
const normalized = normalizeCall(raw);
|
||||
const key = extractKey(normalized);
|
||||
|
||||
if (!callMap.has(normalized)) {
|
||||
callMap.set(normalized, {
|
||||
call: normalized,
|
||||
key: key ?? '',
|
||||
places: [],
|
||||
function stripIllegalReturn(code) {
|
||||
return code.replace(/^\s*return\s+[^;]+;\s*$/gm, (match, offset, input) => {
|
||||
const after = input.slice(offset + match.length).trim();
|
||||
return after === '' ? '' : match;
|
||||
});
|
||||
}
|
||||
|
||||
callMap.get(normalized).places.push(location);
|
||||
}
|
||||
}
|
||||
|
||||
const result = [...callMap.values()];
|
||||
await fs.mkdir(path.dirname(outputFile), { recursive: true });
|
||||
await fs.writeFile(outputFile, JSON.stringify(result, null, 2), 'utf8');
|
||||
|
||||
console.log(`✅ Найдено ${result.length} уникальных вызовов _(...). Сохранено в ${outputFile}`);
|
||||
}
|
||||
|
||||
extractAllUnderscoreCallsWithLocations().catch(console.error);
|
||||
const files = await glob([
|
||||
'src/**/*.ts',
|
||||
'../luci-app-podkop/htdocs/luci-static/resources/view/podkop/**/*.js',
|
||||
], {
|
||||
ignore: [
|
||||
'**/*.test.ts',
|
||||
'**/main.js',
|
||||
'../luci-app-podkop/htdocs/luci-static/resources/view/podkop/main.js',
|
||||
],
|
||||
absolute: true,
|
||||
});
|
||||
|
||||
const results = {};
|
||||
|
||||
for (const file of files) {
|
||||
const contentRaw = await fs.readFile(file, 'utf8');
|
||||
const content = stripIllegalReturn(contentRaw);
|
||||
const relativePath = path.relative(process.cwd(), file);
|
||||
|
||||
let ast;
|
||||
try {
|
||||
ast = parse(content, {
|
||||
sourceType: 'module',
|
||||
plugins: file.endsWith('.ts') ? ['typescript'] : [],
|
||||
});
|
||||
} catch (e) {
|
||||
console.warn(`⚠️ Parse error in ${relativePath}, skipping`);
|
||||
continue;
|
||||
}
|
||||
|
||||
traverse.default(ast, {
|
||||
CallExpression(path) {
|
||||
if (t.isIdentifier(path.node.callee, { name: '_' })) {
|
||||
const arg = path.node.arguments[0];
|
||||
if (t.isStringLiteral(arg)) {
|
||||
const key = arg.value.trim();
|
||||
if (!key) return; // ❌ пропустить пустые ключи
|
||||
const location = `${relativePath}:${path.node.loc?.start.line ?? '?'}`;
|
||||
|
||||
if (!results[key]) {
|
||||
results[key] = { call: key, key, places: [] };
|
||||
}
|
||||
|
||||
results[key].places.push(location);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const outFile = 'locales/calls.json';
|
||||
const sorted = Object.values(results).sort((a, b) => a.key.localeCompare(b.key)); // 🔤 сортировка по ключу
|
||||
|
||||
await fs.mkdir(path.dirname(outFile), { recursive: true });
|
||||
await fs.writeFile(outFile, JSON.stringify(sorted, null, 2), 'utf8');
|
||||
console.log(`✅ Extracted ${sorted.length} translations to ${outFile}`);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -20,6 +20,8 @@
|
||||
"locales:actualize": "yarn locales:exctract-calls && yarn locales:generate-pot && yarn locales:generate-po:ru && yarn locales:distribute"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/parser": "7.28.4",
|
||||
"@babel/traverse": "7.28.4",
|
||||
"@typescript-eslint/eslint-plugin": "8.45.0",
|
||||
"@typescript-eslint/parser": "8.45.0",
|
||||
"chokidar": "4.0.3",
|
||||
|
||||
@@ -2,6 +2,78 @@
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@babel/code-frame@^7.27.1":
|
||||
version "7.27.1"
|
||||
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.27.1.tgz#200f715e66d52a23b221a9435534a91cc13ad5be"
|
||||
integrity sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==
|
||||
dependencies:
|
||||
"@babel/helper-validator-identifier" "^7.27.1"
|
||||
js-tokens "^4.0.0"
|
||||
picocolors "^1.1.1"
|
||||
|
||||
"@babel/generator@^7.28.3":
|
||||
version "7.28.3"
|
||||
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.28.3.tgz#9626c1741c650cbac39121694a0f2d7451b8ef3e"
|
||||
integrity sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==
|
||||
dependencies:
|
||||
"@babel/parser" "^7.28.3"
|
||||
"@babel/types" "^7.28.2"
|
||||
"@jridgewell/gen-mapping" "^0.3.12"
|
||||
"@jridgewell/trace-mapping" "^0.3.28"
|
||||
jsesc "^3.0.2"
|
||||
|
||||
"@babel/helper-globals@^7.28.0":
|
||||
version "7.28.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-globals/-/helper-globals-7.28.0.tgz#b9430df2aa4e17bc28665eadeae8aa1d985e6674"
|
||||
integrity sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==
|
||||
|
||||
"@babel/helper-string-parser@^7.27.1":
|
||||
version "7.27.1"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687"
|
||||
integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==
|
||||
|
||||
"@babel/helper-validator-identifier@^7.27.1":
|
||||
version "7.27.1"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz#a7054dcc145a967dd4dc8fee845a57c1316c9df8"
|
||||
integrity sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==
|
||||
|
||||
"@babel/parser@7.28.4", "@babel/parser@^7.27.2", "@babel/parser@^7.28.3", "@babel/parser@^7.28.4":
|
||||
version "7.28.4"
|
||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.4.tgz#da25d4643532890932cc03f7705fe19637e03fa8"
|
||||
integrity sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==
|
||||
dependencies:
|
||||
"@babel/types" "^7.28.4"
|
||||
|
||||
"@babel/template@^7.27.2":
|
||||
version "7.27.2"
|
||||
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.27.2.tgz#fa78ceed3c4e7b63ebf6cb39e5852fca45f6809d"
|
||||
integrity sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.27.1"
|
||||
"@babel/parser" "^7.27.2"
|
||||
"@babel/types" "^7.27.1"
|
||||
|
||||
"@babel/traverse@7.28.4":
|
||||
version "7.28.4"
|
||||
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.28.4.tgz#8d456101b96ab175d487249f60680221692b958b"
|
||||
integrity sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.27.1"
|
||||
"@babel/generator" "^7.28.3"
|
||||
"@babel/helper-globals" "^7.28.0"
|
||||
"@babel/parser" "^7.28.4"
|
||||
"@babel/template" "^7.27.2"
|
||||
"@babel/types" "^7.28.4"
|
||||
debug "^4.3.1"
|
||||
|
||||
"@babel/types@^7.27.1", "@babel/types@^7.28.2", "@babel/types@^7.28.4":
|
||||
version "7.28.4"
|
||||
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.28.4.tgz#0a4e618f4c60a7cd6c11cb2d48060e4dbe38ac3a"
|
||||
integrity sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==
|
||||
dependencies:
|
||||
"@babel/helper-string-parser" "^7.27.1"
|
||||
"@babel/helper-validator-identifier" "^7.27.1"
|
||||
|
||||
"@esbuild/aix-ppc64@0.25.10":
|
||||
version "0.25.10"
|
||||
resolved "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.10.tgz#ee6b7163a13528e099ecf562b972f2bcebe0aa97"
|
||||
@@ -245,7 +317,7 @@
|
||||
wrap-ansi "^8.1.0"
|
||||
wrap-ansi-cjs "npm:wrap-ansi@^7.0.0"
|
||||
|
||||
"@jridgewell/gen-mapping@^0.3.2":
|
||||
"@jridgewell/gen-mapping@^0.3.12", "@jridgewell/gen-mapping@^0.3.2":
|
||||
version "0.3.13"
|
||||
resolved "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz#6342a19f44347518c93e43b1ac69deb3c4656a1f"
|
||||
integrity sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==
|
||||
@@ -263,7 +335,7 @@
|
||||
resolved "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba"
|
||||
integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==
|
||||
|
||||
"@jridgewell/trace-mapping@^0.3.24":
|
||||
"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.28":
|
||||
version "0.3.31"
|
||||
resolved "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz#db15d6781c931f3a251a3dac39501c98a6082fd0"
|
||||
integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==
|
||||
@@ -996,18 +1068,7 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
|
||||
resolved "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
|
||||
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
|
||||
|
||||
fast-glob@3.3.3:
|
||||
version "3.3.3"
|
||||
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818"
|
||||
integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==
|
||||
dependencies:
|
||||
"@nodelib/fs.stat" "^2.0.2"
|
||||
"@nodelib/fs.walk" "^1.2.3"
|
||||
glob-parent "^5.1.2"
|
||||
merge2 "^1.3.0"
|
||||
micromatch "^4.0.8"
|
||||
|
||||
fast-glob@^3.3.2:
|
||||
fast-glob@3.3.3, fast-glob@^3.3.2:
|
||||
version "3.3.3"
|
||||
resolved "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818"
|
||||
integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==
|
||||
@@ -1226,6 +1287,11 @@ joycon@^3.1.1:
|
||||
resolved "https://registry.npmmirror.com/joycon/-/joycon-3.1.1.tgz#bce8596d6ae808f8b68168f5fc69280996894f03"
|
||||
integrity sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==
|
||||
|
||||
js-tokens@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
|
||||
|
||||
js-tokens@^9.0.1:
|
||||
version "9.0.1"
|
||||
resolved "https://registry.npmmirror.com/js-tokens/-/js-tokens-9.0.1.tgz#2ec43964658435296f6761b34e10671c2d9527f4"
|
||||
@@ -1238,6 +1304,11 @@ js-yaml@^4.1.0:
|
||||
dependencies:
|
||||
argparse "^2.0.1"
|
||||
|
||||
jsesc@^3.0.2:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d"
|
||||
integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==
|
||||
|
||||
json-buffer@3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.npmmirror.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user