The Vue SPA had been reading from frontend/src/locales/*.json while the
Go binary still loaded web/translation/translate.*.toml — and a
sync-locales.mjs pre-build step kept the two in lockstep, with TOML as
the source of truth. Now that go-i18n v2.6.1 already flattens nested
JSON via recGetMessages/addChildMessages, both runtimes can share one
file per locale.
- Move the 13 nested-JSON locale files to web/translation/<lang>.json
so they live alongside the Go //go:embed translation/* directive.
- Switch web/locale/locale.go from toml.Unmarshal to json.Unmarshal
(and drop the pelletier/go-toml import — it's now indirect-only).
Confirmed via a smoke test that pages.index.cpu, subscription.title,
tgbot.commands.help, and menu.settings all resolve in en-US, fa-IR,
ru-RU, and zh-CN.
- Repoint Vue's i18n loader at the new path (../../../web/translation/
*.json glob) and drop the moved-here pathDelimiter comment that no
longer applies.
- Delete the 13 legacy translate.*.toml files and the sync-locales.mjs
script + its npm pre-script hooks (predev/prebuild/i18n:sync). The
Telegram bot and subscription page still get their messages because
they were reading the same MessageIDs the JSON files now produce.
- Update copilot-instructions.md so the next contributor knows where
the canonical translation files live.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The backup modal header was the second-longest title in the dashboard
on every locale ("Database Backup & Restore" / "Резервне копіювання
та відновлення бази даних" / etc). Drop the "Database / Veritabanı /
数据库" qualifier — the modal already lives under the "Database"
column, so the shorter form reads cleaner on narrow viewports.
Updated both the .toml source-of-truth files and the synced .json
locales (re-running scripts/sync-locales.mjs).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Continues the page-by-page translation pass started in cb37dd55 — runs
every user-visible string on settings (General/Security/Telegram/Sub),
inbounds (Client/QR/Info modals), and xray (Routing/Balancer/Rule/Warp/
Nord/Basics/Outbounds tabs) through useI18n. Updates the TOML→JSON sync
script to escape `@` (vue-i18n parses it as a linked-format prefix) and
refreshes all 13 locale files.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Sets up vue-i18n on top of the panel's existing TOML translation
files. The Go side stays the source of truth — translators continue
to edit web/translation/*.toml; a sync script snapshots those files
into per-locale JSON the Vue bundle imports. The login page is
translated end-to-end as a worked example; remaining pages can be
converted incrementally without infrastructure churn.
What's in the box:
- scripts/sync-locales.mjs: small TOML→JSON converter that walks
web/translation/*.toml and writes frontend/src/locales/<code>.json.
Handles the narrow subset of TOML the panel uses (flat key/value
pairs + dotted [section.subsection] heads). Wired as a `prebuild`
+ `predev` script so production builds always include the latest
strings without a manual step.
- src/i18n/index.js: createI18n() in composition mode with all 13
locales emitted as their own Vite chunks. The active locale (read
from the same `lang` cookie LanguageManager has always managed)
plus the en-US fallback are eagerly loaded; the rest are
dynamically importable via a loadLocale(code) helper. This keeps
the per-page bundle the user actually downloads small — only ~30
KB of strings end up in the initial payload, vs ~220 KB if all
13 were eager.
- All five page entries (index/login/settings/inbounds/xray) wire
the i18n plugin into createApp via .use(i18n).
- LoginPage.vue: t(...) replaces hardcoded English on the username
/ password / 2FA placeholders, the submit button label, and the
Settings popover title. The Hello/Welcome headline cycle stays
hardcoded — those are stylistic, not labels.
The 'Hello'/'Welcome' cycle stays in English deliberately; the rest
of the migration's components still ship hardcoded English and will
be converted page by page in follow-up commits.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>