| 2026-10-09 |

webclient: the sidebar in en, uk and ru
...
The third area of the interface translation. AppSidebar, SessionList and
SessionItem now build their text through t(): New Chat, the profile strip and
its filter buttons, the list label (Conversations / Service sessions, and
"30 results" while searching), the empty and loading states, the per-session
icons' tooltips (pinned, scheduled recall, service session), the row actions
(pin / unpin / cancel recall / skip next / delete and its confirm dialog), and
the footer identity (Local mode / Log out / Log in / Settings / Admin).
Also the shared time labels in composables/useTime.js — "just now" and
"5 min ago" — which the messages area renders too; they go to common.*
because both screens say the same words. The label is a cached string, not a
template that re-renders, so the composable now watches locale and rewrites
itself when the language changes. The absolute fallbacks format against
locale.value instead of the browser locale.
31 new slugs: 28 in sidebar.*, plus common.delete, common.minutesAgo (plural)
and confirm.deleteSession. English values are verbatim the old literals, so
the English interface is unchanged and no test needed editing.
Checked live in Chromium against the built bundle on the dev backend: the
sidebar was read in all three languages both in the plain list and with the
search field open, including every title / aria-label / placeholder / alt, with
a slug-leak scan as the pass condition.
One English string stays put on purpose: GnSearchField's aria-label
="Clear search" is baked into the UI kit component, not passed in, so it is
English on every gnexus client; docs/i18n.md now says so.
Eugene Sukhodolskiy
committed
1 day ago
|

webclient: the artifacts drawer in en, uk and ru
...
The second area of the interface translation. Every user-visible string in
ArtifactsPanel.vue now goes through t(): the four tab chips, the empty states,
the action labels (open preview / open raw file / download / copy link), the
pull-to-refresh captions, the background-task badges and ages, the terminal
detail rows and the live-output header.
60 new slugs, in all three dictionaries:
- 39 in artifacts.*;
- 11 in the shared col.* vocabulary (Tool, Status, Started, Finished, PID,
Command, CWD, Uptime, Background, Reason, Sub-agent tokens) — the drawer is
where these table headers first appear;
- 10 in common.* — Download, Yes, No, N/A, the tool-output labels Result and
Live output, and the four the sidebar needs as well: the three
pull-to-refresh captions and "just now". These are the same words on both
screens, so they sit in the shared vocabulary area and the drawer keeps only
what is about the drawer.
Four details worth knowing:
- The English values are verbatim the literals the code hard-coded, so the
English interface is byte-identical and the existing tests stay untouched.
- Counted strings use dictionary plurals: '{n} links in session' is
one/few/many in ru and uk, one/other in en.
- Times (formatTime / formatDate) and ages now format against locale.value
instead of the browser locale, so a Russian interface shows 16:56 where
English shows 04:56 PM.
- Unit suffixes in formatUptime (s/m/h) are left alone: they are symbols
inside a number, not words.
Checked live in Chromium against the built bundle on the dev backend: the
drawer was driven through all four tabs in all three languages on two real
sessions — one with published STL files and a viewed-images group, one with
eight extracted links — with a slug-leak scan as the pass condition, and the
time/age formatting difference confirmed in the rendering.
Eugene Sukhodolskiy
committed
1 day ago
|

webclient: the settings screen in en, uk and ru
...
Settings is the first area off hard-coded English: 122 strings for the shell and
its five tabs — App, Account, Notifications, Synapse and MCP — plus the 43 the
shared vocabulary contributes (common.*, col.*, confirm.*, toast.*).
The English dictionary carries the very literals the templates used, so the
interface a test reads is byte-for-byte what it was and the 56 assertions on
English UI text stay green; only a slug that is genuinely counted had to be
restructured into a plural. In ru and uk the counted strings get the three
Slavic forms driven by n. Product names are never translated: Navi, Synapse,
MCP, Ollama, Tool call, Subagent and Token read the same in all three
languages, and the settings screen keeps one settings.* area with
panel-prefixed slugs rather than a slug per panel, as the other gnexus clients
do.
Strings carrying markup are split around it instead of hiding tags in the
dictionary: the install steps keep their inline icons and <code>http://</code>
in the template, with the words of each sentence on either side. Four lists of
labels — the settings tabs, two table headers, and the reaction options — are
now computed rather than module-level constants, since a list built at import
keeps the language that was active then and ignores a switch. Dates in the
panels go through the interface language as well, not the browser's.
Checked in Chromium against the built bundle: all three languages on all five
tabs, with a scan for a slug leaking into the rendered text as the pass
condition, and the account left with no override afterwards.
Eugene Sukhodolskiy
committed
1 day ago
|

i18n: en/uk/ru, and the interface language of the account
...
The client hard-coded English in every template. It now goes through t() from
src/i18n/ — a locale ref, {param} interpolation, plural dictionaries driven by
n, and flat per-area dictionaries in src/i18n/messages/ carrying the same slugs
in all three languages. vue-i18n is deliberately not pulled in: the platform
handbook rules it out, and what it would buy — plural forms, a reactive locale,
an English fallback — is about eighty lines here. A slug nobody carries falls
back to English, so a screen already translated and one still speaking
hard-coded English can coexist while the work goes area by area.
The language comes from locale_effective in GET /auth/me: the choice made in
Settings (navi_users.locale_override, a new column) over the gnexus-auth
account locale (navi_users.locale, a straight mirror the user.profile_updated
webhook keeps writing — the override sits in its own column precisely so that
mirror cannot wipe it), over en. PATCH /auth/me writes the override, and null
means "follow the account" again. With NAVI_AUTH_ENABLED=false there is no
account to follow, so the choice is kept in localStorage under navi.locale.
The picker is the first block of Settings → Account. Each language is named in
its own language, which is why those three strings are identical in all three
dictionaries. Lists of labels are computed rather than module-level constants:
one built at import would keep the language that was active then and ignore a
switch.
Checked in Chromium against the built bundle: choosing Русский rewrites
<html lang> and the document title, survives a reload, and Auto brings the
account's own language back.
Eugene Sukhodolskiy
committed
1 day ago
|