chore: bump version to 1.14.3, fix translations and cleanup i18n
All checks were successful
Deploy to Production / deploy (push) Successful in 16s

This commit is contained in:
2026-02-13 07:42:17 +01:00
parent 18568fa4ae
commit b3e2f5d022
4 changed files with 159 additions and 74 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "vue-nonograms-solid",
"version": "1.14.2",
"version": "1.14.3",
"homepage": "https://nonograms.7u.pl/",
"type": "module",
"scripts": {

View File

@@ -486,8 +486,8 @@ onUnmounted(() => {
max="99"
/>
<div class="threshold-preview">
<span>Low Density</span>
<span>High Density</span>
<span>{{ t('image.lowDensity') }}</span>
<span>{{ t('image.highDensity') }}</span>
</div>
</div>

View File

@@ -49,6 +49,8 @@ const messages = {
'image.calculating': 'Obliczanie...',
'image.calculatingSolvability': 'Obliczanie rozwiązywalności...',
'image.create': 'STWÓRZ NONOGRAM',
'image.lowDensity': 'Niska gęstość',
'image.highDensity': 'Wysoka gęstość',
'difficulty.easy': 'Łatwy',
'difficulty.medium': 'Średni',
'difficulty.hard': 'Trudny',
@@ -229,10 +231,31 @@ const messages = {
'custom.sizeError': 'Size must be between 5 and 80!',
'custom.fillRate': 'Fill Rate',
'custom.difficulty': 'Difficulty',
'image.title': 'Import Image',
'image.drop': 'Drag image here',
'image.select': 'Select File',
'image.camera': 'Take Photo',
'image.capture': 'Capture',
'image.switch': 'Switch Camera',
'image.cameraError': 'Cannot access camera',
'image.change': 'Change Image',
'image.size': 'Grid Size',
'image.threshold': 'Threshold',
'image.solvability': 'Solvability',
'image.warning': 'This image might require guessing!',
'image.difficulty': 'Difficulty',
'image.calculating': 'Calculating...',
'image.calculatingSolvability': 'Calculating solvability...',
'image.create': 'CREATE NONOGRAM',
'image.lowDensity': 'Low Density',
'image.highDensity': 'High Density',
'difficulty.easy': 'Easy',
'difficulty.medium': 'Medium',
'difficulty.hard': 'Hard',
'difficulty.harder': 'Harder',
'difficulty.hardest': 'Hardest',
'difficulty.extreme': 'Extreme',
'difficulty.unknown': 'Unknown',
'win.title': 'CONGRATULATIONS!',
'win.message': 'You solved the puzzle!',
'win.time': 'Time:',
@@ -404,10 +427,31 @@ const messages = {
'custom.sizeError': '尺寸必须在 5 到 80 之间!',
'custom.fillRate': '填充率',
'custom.difficulty': '难度',
'image.title': 'IMPORT IMAGE',
'image.drop': 'Drag image here',
'image.select': 'Select File',
'image.camera': 'Take Photo',
'image.capture': 'Capture',
'image.switch': 'Switch Camera',
'image.cameraError': 'Cannot access camera',
'image.change': 'Change Image',
'image.size': 'Grid Size',
'image.threshold': 'Threshold',
'image.solvability': 'Solvability',
'image.warning': 'This image might require guessing!',
'image.difficulty': 'Difficulty',
'image.calculating': 'Calculating...',
'image.calculatingSolvability': 'Calculating solvability...',
'image.create': 'CREATE NONOGRAM',
'image.lowDensity': 'Low Density',
'image.highDensity': 'High Density',
'difficulty.easy': '简单',
'difficulty.medium': '中等',
'difficulty.hard': '困难',
'difficulty.harder': '较难',
'difficulty.hardest': '最难',
'difficulty.extreme': '极限',
'difficulty.unknown': '未知',
'win.title': '恭喜!',
'win.message': '你解开了谜题!',
'win.time': '时间:',
@@ -462,20 +506,6 @@ const messages = {
'simulation.table.density': '密度',
'simulation.table.solved': '已解(逻辑)',
'simulation.empty': '点击开始运行蒙特卡罗模拟',
'custom.simulationHelp': 'How is this calculated?',
'custom.hideMap': 'Hide difficulty map',
'custom.showMap': 'Show difficulty map',
'simulation.title': 'Difficulty Simulation',
'simulation.status.ready': 'Ready',
'simulation.status.stopped': 'Stopped',
'simulation.status.completed': 'Completed',
'simulation.status.simulating': 'Simulating {size}x{size} @ {density}%',
'simulation.start': 'Start Simulation',
'simulation.stop': 'Stop',
'simulation.table.size': 'Size',
'simulation.table.density': 'Density',
'simulation.table.solved': 'Solved (Logic)',
'simulation.empty': 'Press Start to run Monte Carlo simulation',
'language.de': 'German',
'language.it': 'Italian',
@@ -627,34 +657,8 @@ const messages = {
'language.searchPlaceholder': '輸入語言名稱...',
'nav.newGame': '新遊戲',
'nav.guide': '指南',
'custom.simulationHelp': 'How is this calculated?',
'custom.hideMap': 'Hide difficulty map',
'custom.showMap': 'Show difficulty map',
'simulation.title': 'Difficulty Simulation',
'simulation.status.ready': 'Ready',
'simulation.status.stopped': 'Stopped',
'simulation.status.completed': 'Completed',
'simulation.status.simulating': 'Simulating {size}x{size} @ {density}%',
'simulation.start': 'Start Simulation',
'simulation.stop': 'Stop',
'simulation.table.size': 'Size',
'simulation.table.density': 'Density',
'simulation.table.solved': 'Solved (Logic)',
'simulation.empty': 'Press Start to run Monte Carlo simulation',
'custom.simulationHelp': 'How is this calculated?',
'custom.hideMap': 'Hide difficulty map',
'custom.showMap': 'Show difficulty map',
'simulation.title': 'Difficulty Simulation',
'simulation.status.ready': 'Ready',
'simulation.status.stopped': 'Stopped',
'simulation.status.completed': 'Completed',
'simulation.status.simulating': 'Simulating {size}x{size} @ {density}%',
'simulation.start': 'Start Simulation',
'simulation.stop': 'Stop',
'simulation.table.size': 'Size',
'simulation.table.density': 'Density',
'simulation.table.solved': 'Solved (Logic)',
'simulation.empty': 'Press Start to run Monte Carlo simulation',
'language.de': 'German',
'language.it': 'Italian',
@@ -819,20 +823,7 @@ const messages = {
'simulation.table.density': 'घनत्व',
'simulation.table.solved': 'हल (तर्क)',
'simulation.empty': 'मोंटे कार्लो सिमुलेशन चलाने के लिए स्टार्ट दबाएँ',
'custom.simulationHelp': 'How is this calculated?',
'custom.hideMap': 'Hide difficulty map',
'custom.showMap': 'Show difficulty map',
'simulation.title': 'Difficulty Simulation',
'simulation.status.ready': 'Ready',
'simulation.status.stopped': 'Stopped',
'simulation.status.completed': 'Completed',
'simulation.status.simulating': 'Simulating {size}x{size} @ {density}%',
'simulation.start': 'Start Simulation',
'simulation.stop': 'Stop',
'simulation.table.size': 'Size',
'simulation.table.density': 'Density',
'simulation.table.solved': 'Solved (Logic)',
'simulation.empty': 'Press Start to run Monte Carlo simulation',
'language.de': 'German',
'language.it': 'Italian',
@@ -1006,20 +997,7 @@ const messages = {
'simulation.table.density': 'Densidad',
'simulation.table.solved': 'Resuelto (Lógica)',
'simulation.empty': 'Pulsa Iniciar para ejecutar la simulación Monte Carlo',
'custom.simulationHelp': 'How is this calculated?',
'custom.hideMap': 'Hide difficulty map',
'custom.showMap': 'Show difficulty map',
'simulation.title': 'Difficulty Simulation',
'simulation.status.ready': 'Ready',
'simulation.status.stopped': 'Stopped',
'simulation.status.completed': 'Completed',
'simulation.status.simulating': 'Simulating {size}x{size} @ {density}%',
'simulation.start': 'Start Simulation',
'simulation.stop': 'Stop',
'simulation.table.size': 'Size',
'simulation.table.density': 'Density',
'simulation.table.solved': 'Solved (Logic)',
'simulation.empty': 'Press Start to run Monte Carlo simulation',
'language.de': 'German',
'language.it': 'Italian',

107
verify_i18n.js Normal file
View File

@@ -0,0 +1,107 @@
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import { execSync } from 'child_process';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// 1. Parse useI18n.js to get keys
const i18nPath = path.join(__dirname, 'src/composables/useI18n.js');
const i18nContent = fs.readFileSync(i18nPath, 'utf8');
function extractKeys(lang) {
// Find start of lang block: " en: {"
const startRegex = new RegExp(`\\s+${lang}:\\s*\\{`);
const startMatch = i18nContent.match(startRegex);
if (!startMatch) return new Set();
const startIndex = startMatch.index + startMatch[0].length;
let braceCount = 1;
let inString = false;
let stringChar = '';
let endIndex = -1;
for (let i = startIndex; i < i18nContent.length; i++) {
const char = i18nContent[i];
if (inString) {
if (char === stringChar && i18nContent[i-1] !== '\\') {
inString = false;
}
} else {
if (char === "'" || char === '"' || char === '`') {
inString = true;
stringChar = char;
} else if (char === '{') {
braceCount++;
} else if (char === '}') {
braceCount--;
if (braceCount === 0) {
endIndex = i;
break;
}
}
}
}
if (endIndex === -1) return new Set();
const block = i18nContent.substring(startIndex, endIndex);
const keys = new Set();
const keyRegex = /['"]([\w.-]+)['"]\s*:/g;
let match;
while ((match = keyRegex.exec(block)) !== null) {
keys.add(match[1]);
}
return keys;
}
const enKeys = extractKeys('en');
console.log(`Found ${enKeys.size} keys in en block.`);
if (enKeys.has('image.title')) {
console.log("'image.title' IS present in en block.");
} else {
console.log("'image.title' is MISSING in en block.");
}
// 2. Scan src for usages
try {
const grepOutput = execSync(`grep -r "t(['\\"]" src | grep -v "node_modules"`, { encoding: 'utf8' });
const usedKeys = new Set();
const usageRegex = /t\(['"]([\w.-]+)['"]/g;
const lines = grepOutput.split('\n');
for (const line of lines) {
let m;
while ((m = usageRegex.exec(line)) !== null) {
usedKeys.add(m[1]);
}
}
console.log(`Found ${usedKeys.size} used keys in src.`);
// 3. Compare
const missingKeys = [];
for (const key of usedKeys) {
// Skip dynamic keys or composed keys if any (heuristic)
if (key.includes('${')) continue;
if (!enKeys.has(key)) {
missingKeys.push(key);
}
}
if (missingKeys.length > 0) {
console.log("Missing translations in en:");
missingKeys.forEach(k => console.log(` - ${k}`));
} else {
console.log("No missing translations found in en.");
}
} catch (e) {
console.error("Error running grep:", e);
}