From eee905e78720add80dbe12e6ead94e5fc075057f Mon Sep 17 00:00:00 2001 From: German Khokhlov Date: Mon, 12 Oct 2026 18:11:20 +0400 Subject: [PATCH] fix(i18n): translate visual preset parameter sliders MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The slider labels (Sensitivity, Bloom, Pixelation, …) live in the shader modules and were rendered raw, so they stayed English in every locale. Mark them with msg() and render them through t(); add es and ru entries. WHOT/BHOT and Ironbow keep their names. Russian Bloom is now «Свечение». Co-Authored-By: Claude Opus 5.5 --- scripts/package-boundaries.json | 12 ++++++++++-- src/hudA11y.test.mjs | 4 ++-- src/i18n/locales/es.json | 16 +++++++++++++++- src/i18n/locales/ru.json | 18 ++++++++++++++++-- src/styles/anime.js | 6 ++++-- src/styles/noir.js | 8 +++++--- src/styles/retro.js | 8 +++++--- src/styles/snow.js | 6 ++++-- src/styles/surveillance.js | 10 ++++++---- src/styles/thermal.js | 12 +++++++----- src/ui/styleParameters.js | 6 ++++-- 11 files changed, 78 insertions(+), 28 deletions(-) diff --git a/scripts/package-boundaries.json b/scripts/package-boundaries.json index 99ebe1d1f..c3970214a 100644 --- a/scripts/package-boundaries.json +++ b/scripts/package-boundaries.json @@ -386,7 +386,11 @@ "modules": [ "src/ui/visualInput.js", "src/ui/applicationShortcuts.js", - "src/ui/styleParameters.js" + "src/ui/styleParameters.js", + "src/i18n/index.js", + "src/i18n/locale.js", + "src/i18n/locales/es.json", + "src/i18n/locales/ru.json" ], "external": [] }, @@ -407,7 +411,11 @@ "src/styles/thermal.js", "src/styles/anime.js", "src/styles/noir.js", - "src/styles/snow.js" + "src/styles/snow.js", + "src/i18n/index.js", + "src/i18n/locale.js", + "src/i18n/locales/es.json", + "src/i18n/locales/ru.json" ], "external": ["cesium"] }, diff --git a/src/hudA11y.test.mjs b/src/hudA11y.test.mjs index 920933852..0c0fd6fe2 100644 --- a/src/hudA11y.test.mjs +++ b/src/hudA11y.test.mjs @@ -28,6 +28,6 @@ test('the first-run checkbox keeps its native visible label', () => { }); test('generated style sliders use the visible parameter label as their name', () => { - assert.match(parameters, /label\.textContent\s*=\s*metadata\.label;/); - assert.match(parameters, /slider\.setAttribute\(['"]aria-label['"],\s*metadata\.label\)/); + assert.match(parameters, /label\.textContent\s*=\s*t\(metadata\.label\);/); + assert.match(parameters, /slider\.setAttribute\(['"]aria-label['"],\s*t\(metadata\.label\)\)/); }); diff --git a/src/i18n/locales/es.json b/src/i18n/locales/es.json index 5ccdd163b..3ee5163c1 100644 --- a/src/i18n/locales/es.json +++ b/src/i18n/locales/es.json @@ -859,5 +859,19 @@ "LIVE · Roads: OpenStreetMap · Flow: TomTom · {percent}% cov · Unmatched: simulated": "EN VIVO · Vías: OpenStreetMap · Flujo: TomTom · {percent}% cob. · Sin coincidencia: simulado", "LIVE · Roads: OpenStreetMap · Flow: TomTom · {percent}% cov": "EN VIVO · Vías: OpenStreetMap · Flujo: TomTom · {percent}% cob.", "SIMULATED · Roads: OpenStreetMap · Flow: TomTom (no matches)": "SIMULADO · Vías: OpenStreetMap · Flujo: TomTom (sin coincidencias)", - "Data source unavailable": "Fuente de datos no disponible" + "Data source unavailable": "Fuente de datos no disponible", + "Saturation": "Saturación", + "Edge Thickness": "Grosor de bordes", + "Contrast": "Contraste", + "Grain": "Grano", + "Vignette": "Viñeta", + "Pixelation": "Pixelado", + "Distortion": "Distorsión", + "Instability": "Inestabilidad", + "Wind": "Viento", + "Gain": "Ganancia", + "Scanlines": "Líneas de barrido", + "Sensitivity": "Sensibilidad", + "WHOT/BHOT": "WHOT/BHOT", + "Ironbow": "Ironbow" } diff --git a/src/i18n/locales/ru.json b/src/i18n/locales/ru.json index dd1164bb8..46d99e42a 100644 --- a/src/i18n/locales/ru.json +++ b/src/i18n/locales/ru.json @@ -776,7 +776,7 @@ "Feather": "Растушёвка", "Celestial": "Небо", "Clean UI": "Чистый интерфейс", - "Bloom": "Bloom", + "Bloom": "Свечение", "Sharpen": "Резкость", "Initializing photorealistic world...": "Инициализация фотореалистичного мира...", "NEAREST": "БЛИЖАЙШАЯ", @@ -866,5 +866,19 @@ "Voice model: {pendingId} — click to switch to {target}; applies next session": "Голосовая модель: {pendingId} — нажмите, чтобы переключиться на {target}; вступит в силу со следующей сессии", "Estimated session cost on {modelId} — {responses} response(s). Warns at {warn}, ends the session at {cap}.": "Оценочная стоимость сессии на {modelId} — ответов: {responses}. Предупреждение при {warn}, завершение сессии при {cap}.", "Radio did not start": "Не удалось запустить радио", - "Running command": "Выполнение команды" + "Running command": "Выполнение команды", + "Saturation": "Насыщенность", + "Edge Thickness": "Толщина контуров", + "Contrast": "Контраст", + "Grain": "Зерно", + "Vignette": "Виньетка", + "Pixelation": "Пикселизация", + "Distortion": "Искажение", + "Instability": "Нестабильность", + "Wind": "Ветер", + "Gain": "Усиление", + "Scanlines": "Строки развёртки", + "Sensitivity": "Чувствительность", + "WHOT/BHOT": "WHOT/BHOT", + "Ironbow": "Ironbow" } diff --git a/src/styles/anime.js b/src/styles/anime.js index bfe51a685..deb79c355 100644 --- a/src/styles/anime.js +++ b/src/styles/anime.js @@ -1,3 +1,5 @@ +import { msg } from '../i18n/index.js'; + /** * Anime / Studio Ghibli Style — Cel-shading + Saturation boost * Transforms the world into an animated film look @@ -5,8 +7,8 @@ export const animeShader = { name: 'anime', uniforms: { - saturation: { default: 1.0, min: 0, max: 2, label: 'Saturation' }, - edgeThick: { default: 0.5, min: 0, max: 1, label: 'Edge Thickness' }, + saturation: { default: 1.0, min: 0, max: 2, label: msg('Saturation') }, + edgeThick: { default: 0.5, min: 0, max: 1, label: msg('Edge Thickness') }, }, fragmentShader: /* glsl */ ` uniform sampler2D colorTexture; diff --git a/src/styles/noir.js b/src/styles/noir.js index ff01d58fc..5ea458859 100644 --- a/src/styles/noir.js +++ b/src/styles/noir.js @@ -1,3 +1,5 @@ +import { msg } from '../i18n/index.js'; + /** * Noir Style — Desaturation + High Contrast + Vignette * Classic film noir / detective movie look @@ -5,9 +7,9 @@ export const noirShader = { name: 'noir', uniforms: { - contrastAmt: { default: 1.2, min: 0, max: 2, label: 'Contrast' }, - grainAmt: { default: 0.5, min: 0, max: 1, label: 'Grain' }, - vignetteAmt: { default: 0.5, min: 0, max: 1, label: 'Vignette' }, + contrastAmt: { default: 1.2, min: 0, max: 2, label: msg('Contrast') }, + grainAmt: { default: 0.5, min: 0, max: 1, label: msg('Grain') }, + vignetteAmt: { default: 0.5, min: 0, max: 1, label: msg('Vignette') }, }, fragmentShader: /* glsl */ ` uniform sampler2D colorTexture; diff --git a/src/styles/retro.js b/src/styles/retro.js index 7ff76e1d4..396f95853 100644 --- a/src/styles/retro.js +++ b/src/styles/retro.js @@ -1,3 +1,5 @@ +import { msg } from '../i18n/index.js'; + /** * CRT Terminal — Dynamic CRT Monitor Aesthetic * Pixelation + Bayer dithering + barrel distortion + scanlines + @@ -12,9 +14,9 @@ export const retroShader = { name: 'retro', uniforms: { - pixelation: { default: 5.0, min: 1, max: 10, label: 'Pixelation' }, - distortion: { default: 0, min: 0, max: 1, label: 'Distortion' }, - instability: { default: 0.4, min: 0, max: 1, label: 'Instability' }, + pixelation: { default: 5.0, min: 1, max: 10, label: msg('Pixelation') }, + distortion: { default: 0, min: 0, max: 1, label: msg('Distortion') }, + instability: { default: 0.4, min: 0, max: 1, label: msg('Instability') }, }, fragmentShader: /* glsl */ ` uniform sampler2D colorTexture; diff --git a/src/styles/snow.js b/src/styles/snow.js index e2ada6ad7..f4bfebe86 100644 --- a/src/styles/snow.js +++ b/src/styles/snow.js @@ -1,3 +1,5 @@ +import { msg } from '../i18n/index.js'; + /** * Snow Style — White overlay + Desaturation + Falling particles * Transforms the world into a winter wonderland @@ -5,8 +7,8 @@ export const snowShader = { name: 'snow', uniforms: { - density: { default: 0.6, min: 0, max: 1, label: 'Density' }, - wind: { default: 0.5, min: 0, max: 1, label: 'Wind' }, + density: { default: 0.6, min: 0, max: 1, label: msg('Density') }, + wind: { default: 0.5, min: 0, max: 1, label: msg('Wind') }, }, fragmentShader: /* glsl */ ` uniform sampler2D colorTexture; diff --git a/src/styles/surveillance.js b/src/styles/surveillance.js index 8ab7eb914..e290dddcb 100644 --- a/src/styles/surveillance.js +++ b/src/styles/surveillance.js @@ -1,3 +1,5 @@ +import { msg } from '../i18n/index.js'; + /** * Night Vision / NVG — PVS-14 Image Intensifier Emulation * P43 phosphor green, intensifier tube bloom, circular vignette, @@ -12,10 +14,10 @@ export const nightVisionShader = { name: 'surveillance', uniforms: { - gain: { default: 0.55, min: 0, max: 1, label: 'Gain' }, - bloom: { default: 0.3, min: 0, max: 1, label: 'Bloom' }, - scanlineStr: { default: 1.0, min: 0, max: 1, label: 'Scanlines' }, - pixelation: { default: 2.5, min: 1, max: 6, label: 'Pixelation' }, + gain: { default: 0.55, min: 0, max: 1, label: msg('Gain') }, + bloom: { default: 0.3, min: 0, max: 1, label: msg('Bloom') }, + scanlineStr: { default: 1.0, min: 0, max: 1, label: msg('Scanlines') }, + pixelation: { default: 2.5, min: 1, max: 6, label: msg('Pixelation') }, }, fragmentShader: /* glsl */ ` uniform sampler2D colorTexture; diff --git a/src/styles/thermal.js b/src/styles/thermal.js index fe0796810..90aff5312 100644 --- a/src/styles/thermal.js +++ b/src/styles/thermal.js @@ -1,3 +1,5 @@ +import { msg } from '../i18n/index.js'; + /** * FLIR — Forward Looking Infrared * Military-grade thermal imaging with White-Hot/Black-Hot modes, @@ -14,11 +16,11 @@ export const thermalShader = { name: 'thermal', uniforms: { - sensitivity: { default: 0.75, min: 0, max: 1, label: 'Sensitivity' }, - bloom: { default: 0.65, min: 0, max: 1, label: 'Bloom' }, - mode: { default: 0.0, min: 0, max: 1, label: 'WHOT/BHOT' }, - pixelation: { default: 1.5, min: 1, max: 6, label: 'Pixelation' }, - palette: { default: 0.0, min: 0, max: 1, label: 'Ironbow' }, + sensitivity: { default: 0.75, min: 0, max: 1, label: msg('Sensitivity') }, + bloom: { default: 0.65, min: 0, max: 1, label: msg('Bloom') }, + mode: { default: 0.0, min: 0, max: 1, label: msg('WHOT/BHOT') }, + pixelation: { default: 1.5, min: 1, max: 6, label: msg('Pixelation') }, + palette: { default: 0.0, min: 0, max: 1, label: msg('Ironbow') }, }, fragmentShader: /* glsl */ ` uniform sampler2D colorTexture; diff --git a/src/ui/styleParameters.js b/src/ui/styleParameters.js index 6afa3e36b..9b453a69a 100644 --- a/src/ui/styleParameters.js +++ b/src/ui/styleParameters.js @@ -1,3 +1,5 @@ +import { t } from '../i18n/index.js'; + /** * Own generated parameter rows and their input listeners. * The caller supplies uniform metadata/value access and rendering/persistence @@ -30,11 +32,11 @@ export function createStyleParameters({ row.className = 'param-slider-row'; const label = documentRef.createElement('span'); label.className = 'param-label'; - label.textContent = metadata.label; + label.textContent = t(metadata.label); const slider = documentRef.createElement('input'); slider.type = 'range'; slider.className = 'param-slider'; - slider.setAttribute('aria-label', metadata.label); + slider.setAttribute('aria-label', t(metadata.label)); slider.min = metadata.min; slider.max = metadata.max; slider.step = metadata.max <= 1 ? '0.01' : '0.1';