diff --git a/web/js/i18n.js b/web/js/i18n.js index d277899..4c8bea4 100644 --- a/web/js/i18n.js +++ b/web/js/i18n.js @@ -48,6 +48,11 @@ export function onLangChange(fn) { return () => listeners.delete(fn); } +// Human name for a complexity tier; custom tier names pass through as-is. +export function trTier(tier) { + return ["low", "medium", "high"].includes(tier) ? t("tier." + tier) : tier; +} + export function t(key, vars) { const d = DICTS[lang] || en; let s = d[key] ?? en[key] ?? key; diff --git a/web/js/i18n/de.js b/web/js/i18n/de.js index cc8c5fd..f3b1ecb 100644 --- a/web/js/i18n/de.js +++ b/web/js/i18n/de.js @@ -631,4 +631,7 @@ export default { "routing.tasks.addModel": "Modell", "routing.bands.enable": "Eigene Komplexitätsgrenzen", "routing.bands.hint": "Ziehen Sie die Griffe: wo low endet und high beginnt — die Griffe schieben einander, die Zahlen aktualisieren sich live. Der kontinuierliche Router-Score wird mit IHREN Grenzen neu eingeteilt; ohne Häkchen gelten die Grenzen des Routers.", + "tier.low": "leicht", + "tier.medium": "mittel", + "tier.high": "schwer", }; diff --git a/web/js/i18n/en.js b/web/js/i18n/en.js index e31d584..80c81cf 100644 --- a/web/js/i18n/en.js +++ b/web/js/i18n/en.js @@ -624,4 +624,7 @@ export default { "routing.tasks.addModel": "model", "routing.bands.enable": "My complexity boundaries", "routing.bands.hint": "Drag the handles to set where low ends and where high begins — the handles push each other, the numbers update live. The router's continuous score is re-mapped with YOUR boundaries; unchecked = the router's own bands.", + "tier.low": "low", + "tier.medium": "medium", + "tier.high": "high", }; diff --git a/web/js/i18n/es.js b/web/js/i18n/es.js index a8dbeb9..72e0632 100644 --- a/web/js/i18n/es.js +++ b/web/js/i18n/es.js @@ -631,4 +631,7 @@ export default { "routing.tasks.addModel": "modelo", "routing.bands.enable": "Mis límites de complejidad", "routing.bands.hint": "Arrastra los tiradores: dónde termina low y dónde empieza high — se empujan entre sí y las cifras se actualizan en vivo. La puntuación continua del router se re-mapea con TUS límites; sin marcar = las bandas del propio router.", + "tier.low": "fácil", + "tier.medium": "medio", + "tier.high": "difícil", }; diff --git a/web/js/i18n/fr.js b/web/js/i18n/fr.js index 6d32b89..887d2f7 100644 --- a/web/js/i18n/fr.js +++ b/web/js/i18n/fr.js @@ -631,4 +631,7 @@ export default { "routing.tasks.addModel": "modèle", "routing.bands.enable": "Mes limites de complexité", "routing.bands.hint": "Faites glisser les poignées : où finit low et où commence high — elles se poussent l'une l'autre, les chiffres se mettent à jour en direct. Le score continu du routeur est re-découpé avec VOS limites ; décoché = les bandes du routeur.", + "tier.low": "facile", + "tier.medium": "moyen", + "tier.high": "difficile", }; diff --git a/web/js/i18n/ru.js b/web/js/i18n/ru.js index 9fe231b..47eb081 100644 --- a/web/js/i18n/ru.js +++ b/web/js/i18n/ru.js @@ -623,4 +623,7 @@ export default { "routing.tasks.addModel": "модель", "routing.bands.enable": "Мои границы сложности", "routing.bands.hint": "Тяните ручки: где кончается low и где начинается high — ручки толкают друг друга, цифры обновляются на лету. Непрерывный score роутера пересчитывается ВАШИМИ границами; галочка снята — действуют пороги самого роутера.", + "tier.low": "лёгкий", + "tier.medium": "средний", + "tier.high": "сложный", }; diff --git a/web/js/i18n/tr.js b/web/js/i18n/tr.js index 6492030..5d2bb43 100644 --- a/web/js/i18n/tr.js +++ b/web/js/i18n/tr.js @@ -631,4 +631,7 @@ export default { "routing.tasks.addModel": "model", "routing.bands.enable": "Kendi karmaşıklık sınırlarım", "routing.bands.hint": "Tutamaçları sürükleyin: low nerede biter, high nerede başlar — tutamaçlar birbirini iter, sayılar anlık güncellenir. Yönlendiricinin sürekli puanı SİZİN sınırlarınızla yeniden bölümlenir; işaret kaldırılırsa yönlendiricinin kendi bantları geçerli.", + "tier.low": "kolay", + "tier.medium": "orta", + "tier.high": "zor", }; diff --git a/web/js/i18n/zh.js b/web/js/i18n/zh.js index a86e412..801fafb 100644 --- a/web/js/i18n/zh.js +++ b/web/js/i18n/zh.js @@ -631,4 +631,7 @@ export default { "routing.tasks.addModel": "模型", "routing.bands.enable": "自定义复杂度边界", "routing.bands.hint": "拖动手柄设定 low 到哪里结束、high 从哪里开始 — 手柄会相互推动,数字实时更新。路由器的连续得分将按你的边界重新划分;取消勾选则使用路由器自身的边界。", + "tier.low": "简单", + "tier.medium": "中等", + "tier.high": "困难", }; diff --git a/web/js/views/benchmark.js b/web/js/views/benchmark.js index edeae8e..140d319 100644 --- a/web/js/views/benchmark.js +++ b/web/js/views/benchmark.js @@ -8,7 +8,7 @@ // (keyword checks with | synonyms, think-tag stripping, anti-loop detection, // pass/partial/fail), now embedded, localized and CDN-free. import { h, mount, toast, ms, money, moneyEl } from "../ui.js"; -import { t } from "../i18n.js"; +import { t, trTier } from "../i18n.js"; import { api, testStream } from "../api.js"; // ── built-in suite ────────────────────────────────────────────────────────── @@ -402,7 +402,7 @@ export async function renderBenchmark() { : h("span", { class: r.taskOk ? "" : "err" }, (r.taskOk ? "✓ " : "✗ ") + r.task, r.taskOk ? "" : h("div", { class: "muted small" }, t("bm.expectedLbl") + " " + (r.expected || []).join("/")))), - h("td", {}, r.tier ? h("span", { class: "badge tier-" + r.tier }, r.tier) : "—"), + h("td", {}, r.tier ? h("span", { class: "badge tier-" + r.tier }, trTier(r.tier)) : "—"), h("td", {}, h("span", { class: "mono small" }, r.chosen || "—"), r.cost > 0 ? h("div", { class: "muted small" }, moneyEl(r.cost)) : null, diff --git a/web/js/views/playground.js b/web/js/views/playground.js index 9f4fc56..bb20bc8 100644 --- a/web/js/views/playground.js +++ b/web/js/views/playground.js @@ -5,7 +5,7 @@ // Media results are decoded in the browser (PPM → canvas, MJPEG AVI → canvas // player synced to the wav) — the same no-ffmpeg stance as the engine itself. import { h, mount, toast, money, moneyEl, ms } from "../ui.js"; -import { t } from "../i18n.js"; +import { t, trTier } from "../i18n.js"; import { api, testStream, mediaFileUrl, mediaFileBytes } from "../api.js"; import { appState } from "../app.js"; import { ppmToCanvas, fileToPpmB64, aviJpegFrames, mjpegPlayer } from "../media-utils.js"; @@ -206,7 +206,7 @@ export async function renderPlayground() { decision, h("div", { class: "card-head" }, h("h3", {}, t("pg.decision"))), kv(t("pg.task"), c.task_label || "—"), - kv(t("pg.tier"), c.complexity?.tier ? h("span", { class: "badge tier-" + c.complexity.tier }, c.complexity.tier) : "—"), + kv(t("pg.tier"), c.complexity?.tier ? h("span", { class: "badge tier-" + c.complexity.tier }, trTier(c.complexity.tier)) : "—"), h( "div", { class: "kv", style: "display:block" }, diff --git a/web/js/views/routing.js b/web/js/views/routing.js index 37f97b2..bccf7b5 100644 --- a/web/js/views/routing.js +++ b/web/js/views/routing.js @@ -1,6 +1,6 @@ // Routing — tier editor (ordered model chips), default model, and policy. import { h, mount, toast } from "../ui.js"; -import { t } from "../i18n.js"; +import { t, trTier } from "../i18n.js"; import { api } from "../api.js"; import { appState } from "../app.js"; @@ -153,9 +153,9 @@ export async function renderRouting() { // подписи не наезжают, когда ручки рядом hMed.querySelector(".bands-val").classList.toggle("raised", me - lo < 0.09); mount(legend, - h("span", { class: "badge tier-low" }, `low ≤ ${lo.toFixed(2)}`), - h("span", { class: "badge tier-medium" }, `medium ≤ ${me.toFixed(2)}`), - h("span", { class: "badge tier-high" }, `high > ${me.toFixed(2)}`)); + h("span", { class: "badge tier-low" }, `${trTier("low")} ≤ ${lo.toFixed(2)}`), + h("span", { class: "badge tier-medium" }, `${trTier("medium")} ≤ ${me.toFixed(2)}`), + h("span", { class: "badge tier-high" }, `${trTier("high")} > ${me.toFixed(2)}`)); track.classList.toggle("disabled", !st.bands); } @@ -211,7 +211,7 @@ export async function renderRouting() { return h( "div", { class: "tierrow" }, - h("div", { class: "tname" }, h("span", { class: "badge tier-" + tier }, tier)), + h("div", { class: "tname" }, h("span", { class: "badge tier-" + tier }, trTier(tier))), chipEditor(st.tiers, tier) ); } @@ -254,7 +254,7 @@ export async function renderRouting() { "select", { class: "pair-tier", title: t("routing.tasks.tierHint") }, h("option", { value: "any" }, t("routing.tasks.tierAny")), - ...Object.keys(st.tiers).map((tr) => h("option", { value: tr }, tr)) + ...Object.keys(st.tiers).map((tr) => h("option", { value: tr }, trTier(tr))) ); tierSel.value = p.tier; tierSel.addEventListener("change", () => { p.tier = tierSel.value; });