Skip to content

Section heading keys for all locales, plus English and Spanish corrections - #34

Open
pdalexis wants to merge 6 commits into
electerm:masterfrom
pdalexis:add-settings-section-headings
Open

Section heading keys for all locales, plus English and Spanish corrections#34
pdalexis wants to merge 6 commits into
electerm:masterfrom
pdalexis:add-settings-section-headings

Conversation

@pdalexis

@pdalexis pdalexis commented Aug 14, 2026

Copy link
Copy Markdown

What this is

This started as six new keys and grew, so here is the whole thing in one PR. Six commits. Commit 1 is independent; commits 3-6 are ordered, and commit 6 re-touches three strings from 4 and 5, so it reverts cleanly off the tip but 4 and 5 do not revert in isolation.

commit files
1 Make build and tests runnable on Windows bin/build.js, tests/test.spec.js
2 Add six section heading keys to all locale files all 15
3 English: fix spelling and grammar en_us.js
4 Spanish: fix mistranslations, typos and untranslated strings es_es.js
5 Spanish: one register and one capitalisation rule es_es.js
6 Spanish: settle on "el terminal", not "la terminal" es_es.js

Happy to split this into four PRs instead if you would rather review it that way — say so and I will. The only reason they are together is that commits 3–6 touch lines adjacent to where commit 2 inserts, so as separate PRs they would conflict pairwise.

npm run lint and npm run test pass. Note CI does not run here: .github/workflows/test.yml triggers on build and test, not on PRs to master.


1. Windows

bin/build.js and tests/test.spec.js both dynamic-import each locale by absolute path. On Windows that starts with a drive letter and Node's ESM loader reads c: as a URL scheme:

Error [ERR_UNSUPPORTED_ESM_URL_SCHEME]: Received protocol 'c:'

So neither npm run build nor npm run test runs there at all — which means the npm run test the README asks for before pushing, and the pre-push hook, cannot be honoured on Windows. pathToFileURL on both call sites, two lines, no change on Linux or macOS.

I put this first because without it I could not verify anything else.

2. The six section headings

electerm#4484 groups the SSH bookmark form (sshAuthFields only — the basic, telnet, rdp, vnc, ftp and web forms are untouched) and the Setting toggles under headings. Two of the labels it needs already exist (auth, window); the other six do not. e() falls back to capitalizeFirstLetter(key), so in English five of the six already render acceptably — "Start", "Privacy", "Identity" — and only onConnect is visibly wrong, as "OnConnect". The gain is almost entirely in the other fourteen locales, where a missing key drops an English word into the middle of a translated panel.

key groups
setting.start initDefaultTabOnStart, checkUpdateOnStart
setting.privacy disableConnectionHistory, disableTransferHistory, hideIP
setting.development disableDeveloperTool, debug
form.identity category, title, host, port, username
form.onConnect the login script and the start directories
form.environment setEnv, envLang, encode, description, type

Two are easy to get wrong without that context: setting.start is app startup, not a start button, and form.identity names the connection — it sits above auth and holds no credential.

All fifteen files. The first version of this branch added them to en_us and es_es only, which cannot pass npm run test: tests/test.spec.js compares Object.keys() with _.isEqual across every adjacent pair, so position matters as much as presence. The setting trio goes after common and the form trio after auth in all fifteen.

I wrote en_us and es_es. For the other thirteen I followed each file's own neighbour for capitalisation — common for the setting trio, auth for the form trio — and its existing vocabulary: zh_tw takes 啟動 rather than the 啓 variant it also uses, because the three startup strings this heading sits over all use 啟; fr_fr capitalises the setting trio but not the form trio, because that is what Général and authentification already do. They are six ordinary words, but I do not speak most of these languages — overrule any of them freely, or tell me to drop them and run bin/translate-with-ai.js instead.

3. English

Twenty-eight fixes, no keys touched. en_us.js is what the other fourteen get written from, so I kept to things that are wrong, not things I would word differently.

  • Spelling. hotkey can not be registecannot be registered, and the same can not in tabs.titleEmptyWarn; data transfered totransferred; goto parent foldergo to.
  • A key name reached the UI. form.privateKey was the string 'privateKey' — the camelCase key itself, shown as the field label — and passphraseDesc said passphrase for privateKey. form.loginScriptTip said run script after logined.
  • Number. open all bookmark in this category, allow multi instance, disable developer tool, support terminal image, support screen reader in terminal, and all the file/directory in them, sync bookmark / history / setting.
  • Verb forms. role for create terminal command with AI → creating; when app startwhen the app starts; when switch to sftpwhen switching to; updater.fail was check update fails, but its single call site (upgrade.jsx renderError) is fed by both the version check and the download error handler, so naming the check would be wrong half the time — it is now the neutral update failed. useSystemTitleBarTip was three clauses with no subject and now reads requires restarting the app to take effect; transparency does not work with the system title bar.
  • rest as a bare adjective in mergeDesc / overwriteDesc / renameDesc: merge rest conflict folders. It parses as the noun, and the translations show it was read that way — es_es rendered it "carpetas de conflicto de descanso", folders of conflict of a nap. Now merge the remaining conflicting folders.

I left the file's mixed label capitalisation alone (Workspaces, Expand all, UI Themes against several hundred lowercase). It is cosmetic, it would ripple into every other locale, and it is your call.

4–6. Spanish

I am not the original translator — that is Federico Pereira, credited in the README. #24, #27, #28 and #32 set the precedent for improving an existing language, so I hope this is welcome; route it past him first if you prefer.

Commit 4, sixty-seven value changes. Most are outright errors; a handful (sin objetosin elementos, tiempo de accesofecha de acceso) are word-choice calls. The ones that matter most are three that break when composed at runtime:

  • app.toShow was 'mostrar', and init-app.js renders ${press} ${hotkey} ${toShow} into a desktop notification title — shown under --autohide — which read "presione Ctrl+2 mostrar", with no preposition.
  • sftp.hfd was 'esconder archivos y directorios', and address-bar.jsx renders ${hide|show} ${hfd} — giving "esconder esconder archivos y directorios".
  • sftp.selected was the infinitive 'seleccionar' where file-item.jsx renders ${del}:${selected}(${len}). It is now 'seleccionados', plural to agree with the count.

Then five strings still in English ("proxy ip", "proxy port", "proxy type", "Cursor Blink", and the stray Bookmarks / Start); false friends (resumir = to summarise, for resume; defecto = a defect, for default; llave = a physical key, for the private key, where privateKeyDesc already said clave; camino = a road, for path, two lines below a ruta; correr = to run on foot, for running a command); useSystemTitleBarTip opening in the first person, "Necesito reiniciar la aplicación"; fontFamily as "tipografía familiar", familiar typography; ssh.nextMatch / prevMatch dropping "match" and leaving a search bar labelled "próximo / anterior"; and the settingSync trio with its two nouns reversed, so "download settings" read as "the download settings". Plus typos: minimimizar, autenticacción, omitir esta version, error de inicio de sesion, comando rápidos.

Commit 5, seventy-eight changes. Mostly consistency, plus one outright error that only surfaced once the register was uniform: menu.startspeaking / stopspeaking sit under Electron's macOS Edit ▸ Speech submenu, where the app reads the selection aloud. Anything about dictado would name speech-to-text, the opposite feature; they now read iniciar la locución / detener la locución. Labels take the infinitive and messages address the reader as usted, which is what most of the file already did — the holdouts were tuteo ('Hazlo portátil', '¿Estás seguro?'), usted-imperatives where a label wanted an infinitive ('Elija el archivo' next to 'elegir carpeta'), and third-person indicatives ('Soporta expresiones regulares'). Forty-seven labels started with a capital where English did not, and five were in Title Case, which Spanish does not use — where en_us itself capitalises, Spanish keeps the capital, so the rule is per key rather than blanket lowercase. sftp.delTip also regained the demonstrative English carries in "this will delete these", since it renders as ${delTip} ${names} ${delTip1} (${n}). And one word per concept, always the one the file already used more often: ocultar over esconder, fusionar over unir, actualizar over refrescar.

Commit 6, the one judgement call. Both genders of terminal are correct Spanish and the file used both — eleven masculine against six feminine, sometimes a line apart: nueva terminal in the toolbar, registro del terminal in Setting. Peninsular usage leans masculine and this is es_es, so the six feminine ones move. Separate commit on purpose: drop it and commits 4 and 5 still stand.


Two things I found and did not touch

bin/build.js flattens every section into one namespace, so a key repeated across sections shadows itself. Where the two values differ, the later section silently wins and the earlier one is dead. en_us has none; nine other files do:

de_de  pasteSelected  menu="Auswahl einfügen"  vs setting="Ausgewähltes einfügen"
de_de  clear          transferHistory="Leeren" vs ssh="Löschen"
de_de  selectAll      sftp="Alle auswählen"    vs ssh="Alles auswählen"
fr_fr  homepage       menu="Accueil"           vs control="Page d'accueil"
fr_fr  clear          transferHistory="Vider"  vs ssh="Effacer"
ja_jp  about          menu="アプリについて"      vs common="について"
ko_kr  pasteSelected  menu="선택 항목 붙여넣기"   vs setting="선택한 항목 붙여넣기"
pt_br  selectAll      sftp="selecionar todos"  vs ssh="selecionar tudo"
ru_ru  cancel         common="Отмена"          vs sftp="Отменить"
ru_ru  edit           menu="Правка"            vs sftp="Редактировать"
tr_tr  pasteSelected  menu="Seçileni Yapıştır" vs setting="Seçili olanı yapıştır"
zh_cn  clear          transferHistory="清空"    vs ssh="清屏"
zh_tw  pasteSelected  menu="貼上所選內容"        vs setting="貼上所選"
zh_tw  clear          transferHistory="清空白"   vs ssh="清空畫面"

zh_cn is the clearest symptom: the transfer-history Clear button says 清屏, "clear screen". I only fixed the es_es one, since it was a plain adjective-for-verb slip. The rest need either a native speaker's call or distinct keys, and both are yours to make.

78 keys that src/client calls through e('...') have no entry in en_us.js at all (80 counting two more reached via window.translate) — category, passwords, width, height, fullscreen, replace, domain, qualityLevel and so on. Ten of them are whole English sentences used as keys ('URL must start with http:// or https://', 'file too large for code compare'), which cannot be translated at all. Because e() falls back to the key, English looks fine and only translated UIs show the raw identifier. Nothing to do with this PR; I can open an issue with the full list if that is useful.

@pdalexis pdalexis changed the title Add six section heading keys (en, es) Add six section heading keys (en, es), and one spelling fix Aug 14, 2026
@pdalexis
pdalexis force-pushed the add-settings-section-headings branch from 45cbf2f to b39e8f0 Compare August 14, 2026 21:58
@pdalexis pdalexis changed the title Add six section heading keys (en, es), and one spelling fix Section heading keys for all locales, plus English and Spanish corrections Aug 14, 2026
`bin/build.js` and `tests/test.spec.js` both dynamic-import each locale file
by absolute path. On Windows that path starts with a drive letter, which
Node's ESM loader reads as a URL scheme:

    Error [ERR_UNSUPPORTED_ESM_URL_SCHEME]: Received protocol 'c:'

So `npm run build` and `npm run test` cannot run at all on Windows, which
also means the documented `npm run test` before push cannot be honoured
there. CI runs on ubuntu-latest, so this never surfaced.

Wrapping the path in `pathToFileURL` fixes both. No behaviour change on
Linux or macOS, where an absolute path already resolves.
electerm/electerm#4484 groups the SSH bookmark form fields and the Setting
toggles under headings. Two of the labels it needs already exist, auth and
window; the other six do not.

e() falls back to capitalizeFirstLetter(key) when a key is missing, so in
English five of the six already render acceptably -- "Start", "Privacy",
"Identity" -- and only onConnect is visibly wrong, as "OnConnect". The gain
here is almost entirely in the other fourteen locales, where a missing key
means an English word appears in the middle of a translated panel.

setting.start groups initDefaultTabOnStart and checkUpdateOnStart, so it is
app startup rather than a start button. setting.privacy groups
disableConnectionHistory, disableTransferHistory and hideIP.
setting.development groups disableDeveloperTool and debug.

form.identity groups category, title, host, port and username -- it names
the connection, it is not a credential. form.onConnect groups the login
script and the start directories. form.environment groups setEnv, envLang,
encode, description and type. Only sshAuthFields is grouped in 4484; the
basic, telnet, rdp, vnc, ftp and web forms are untouched.

All fifteen files, not just en_us and es_es. tests/test.spec.js compares
Object.keys() with _.isEqual across every pair of locales, so a key present
in two files and absent from thirteen fails `npm run test` -- which is what
the first version of this branch did. Position matters for the same reason,
so the setting trio sits after `common` and the form trio after `auth` in
all fifteen.

I wrote en_us and es_es. The other thirteen follow each file's own neighbour
for capitalisation -- `common` for the setting trio, `auth` for the form
trio -- and its existing vocabulary: fr_fr capitalises the setting trio and
not the form trio because that is what Général and authentification already
do, and zh_tw takes 啟動 rather than the 啓 variant it also uses, because the
three startup strings this heading sits over all use 啟. Overrule any of the
thirteen freely.
Twenty-eight value changes, no keys touched. en_us is what the other
fourteen files get written from, and nothing here validates a translation
once the English moves, so I kept to things that are wrong rather than
things I would word differently.

Spelling: 'can not be registe' -> 'cannot be registered', and the same
'can not' in tabs.titleEmptyWarn; 'transfered' -> 'transferred'; 'goto
parent folder' -> 'go to'.

form.privateKey read 'privateKey' -- the camelCase key name reached the
radio label the user sees -- and passphraseDesc referred to it the same way.
Both now say 'private key'. form.loginScriptTip said 'after logined'.

Number agreement: 'open all bookmark in this category', 'allow multi
instance', 'disable developer tool', 'support terminal image', 'support
screen reader in terminal', 'and all the file/directory in them', and
syncDesc's 'sync bookmark / history / setting'.

Verb forms: 'role for create terminal command with AI' and 'role for explain
terminal output with AI' -> creating/explaining; 'when app start' -> 'when
the app starts'; 'when switch to sftp' -> 'when switching to'; 'when click
to open terminal link' -> 'when clicking to open'. useSystemTitleBarTip was
three clauses with no subject; it now reads 'requires restarting the app to
take effect; transparency does not work with the system title bar'.

updater.fail was 'check update fails'. Its single call site, upgrade.jsx's
renderError, is fed by two paths -- the version check and the download error
handler -- so naming the check would be wrong half the time. It now reads
'update failed'.

sftp mergeDesc / overwriteDesc / renameDesc used 'rest' as a bare adjective:
'merge rest conflict folders'. It parses as the noun, and the translations
show it -- es_es rendered it 'carpetas de conflicto de descanso', folders of
conflict of a nap. Now 'merge the remaining conflicting folders'.

Also 'create bookmark by ai' -> 'with AI', matching the caps used everywhere
else, and a space before the parenthetical in hotkeyDesc and timeoutDesc,
where 'millisecond' was also singular.
Sixty-seven value changes in es_es.js. No keys added, removed or reordered.

Left in English: form.proxyIp/proxyPort/proxyType ("proxy port"),
setting.cursorBlink ("Cursor Blink"), and the stray "Bookmarks" and "Start"
in setting.onStartBookmarks and setting.showHiddenFilesOnSftpStart.

Broken when composed. app.toShow was 'mostrar' where init-app.js renders
`${press} ${hotkey} ${toShow}` into a desktop notification title, so under
--autohide it read "presione Ctrl+2 mostrar" with no preposition. sftp.hfd
was 'esconder archivos y directorios' where address-bar.jsx renders
`${hide|show} ${hfd}`, giving "esconder esconder archivos y directorios".
sftp.selected was the infinitive 'seleccionar' where file-item.jsx renders
`${del}:${selected}(${len})`, so it now reads 'seleccionados', plural to
agree with the count.

Wrong word. 'camino' (a road) for path, where the same section already used
'ruta'; 'llave' (a physical key) for private key, where privateKeyDesc
already said 'clave'; 'resumir' (to summarise) for resume; 'reiniciar'
(restart) for reset, colliding with menu.restart; 'correr' (to run on foot)
for running a command; 'lenguaje' (the faculty of language) for UI language;
'mejorar' (to improve) for upgrade; 'defecto' (a defect) for default;
'contener' (to contain) for holding down a modifier key; 'sin objeto'
(pointless) for antd's empty-list text.

Mistranslated. "merge rest conflict folders" became "carpetas de conflicto
de descanso" -- "rest" read as a nap -- in mergeDesc and overwriteDesc, and
renameDesc dropped it. "support screen reader" became "lector de pantalla de
soporte". useSystemTitleBarTip opened with 'Necesito', first person: "I need
to restart the app". fontFamily was 'tipografía familiar', familiar
typography. scaleViewport pointed at the window rather than the viewport.
ssh.nextMatch/prevMatch dropped "match", leaving a search bar labelled
'próximo / anterior'. rightClickSelectsWord was word salad.
settingSync.settingSync, syncSettings and downloadSettings had their two
nouns the wrong way round, so "download settings" read as "the download
settings".

Typos and agreement: 'minimimizar', 'autenticacción', 'omitir esta version',
'error de inicio de sesion', 'comando rápidos', sftp.folder pluralised
against a singular source, transferHistory.clear as the adjective 'limpio'.

Two follow the English changes in the previous commit: screenReaderMode and
disableDeveloperTool are now plural. 'IP' is capitalised in proxyIp to match
proxyIpPlaceholder two lines down, and the deep-link pair settles on
'controlador de protocolo', which is the es-ES term, rather than the calque
'manejador' one of them used.

transferHistory.clear is also the file's only divergent duplicate key:
'clear' appears in both transferHistory and ssh, and since bin/build.js
flattens every section into one namespace, ssh's 'limpiar' silently won.
English has no such divergence.
Seventy-eight value changes, no key touched. Mostly making the file agree
with itself, plus one outright error that only shows up once the register is
consistent.

That error: menu.startspeaking and stopspeaking sit under Electron's macOS
Edit > Speech submenu, where the app reads the selection aloud. 'empieza a
hablar' was clumsy but pointed the right way; anything about dictado would
have pointed at speech-to-text, the opposite feature. They now read
'iniciar la locución' and 'detener la locución'.

Register. Labels take the infinitive and messages address the reader as
usted, which is what most of the file already did. The holdouts split three
ways: tuteo ('Hazlo portátil', '¿Estás seguro?', 'estás usando', 'comprueba',
'Usa la barra de título', and form.aiSecurityNotice, which was the last one
left), usted-imperatives where a label wanted an infinitive ('Elija el
archivo' next to 'elegir carpeta', 'cierre la pestaña actual', 'Abra la
pestaña predeterminada', 'Acceda a esta carpeta'), and third-person
indicatives ('Muestra texto en bruto', 'Soporta expresiones regulares',
'distingue mayúsculas y minúsculas', whose two siblings in the same search
bar were already being converted).

Capitalisation. en_us keeps labels lowercase and lets the app capitalise --
window.translate applies capitalizeFirstLetter to every string, and the
section headings are text-transform uppercase -- so a hardcoded capital in
the locale file just makes the sidebar inconsistent. Forty-seven labels
started with a capital where English did not, and five were in Title Case,
which Spanish does not use: 'Tiempo de Ejecución', 'Solo Vista',
'Credenciales Requeridas', 'Nombre del Perfil', 'Contenido Completo'. Where
en_us itself capitalises (Expand all, UI Themes, Edit with color picker)
Spanish keeps the capital, so the rule is per key rather than blanket
lowercase. Full sentences keep theirs.

sftp.delTip renders as `${delTip} ${names} ${delTip1} (${n})`. It had lost
the demonstrative English carries in "this will delete these", and delTip1
said 'y todos los archivos / directorios que contienen', which parses as
"that contain" with no object. Now 'esto borrará estos' -- borrar, matching
the button that opens the dialog -- and 'y todo su contenido'.

One word per concept, picking whichever the file already used more often:
'ocultar' over 'esconder' (hideIP and hideSshConfig were already 'ocultar'),
'fusionar' over 'unir' (mergeAll was already 'fusionar'), 'actualizar' over
'refrescar', 'buscar actualizaciones' over 'comprueba la actualización',
'restablecer el zoom' over 'reajustar', 'aumentar el zoom' to pair with the
existing 'disminuir el zoom'.

updater.fail becomes the neutral 'error al actualizar' rather than naming
the version check, for the reason given in the English commit.

Also 'Expandir todos' -> 'Expandir todo', 'obligatorio' for a required field
rather than 'necesario', 'usar un gist existente' for the dangling 'utilizar
existente', and syncDesc's preposition: 'sincronizar ... a github secret
gist' -> '... con un gist secreto de GitHub'.
Both genders are correct Spanish and the file used both: eleven masculine
against six feminine, sometimes a line apart -- 'nueva terminal' in the
toolbar, 'registro del terminal' in Setting. Peninsular usage for a computer
terminal leans masculine and this is es_es, so the six feminine ones move.

Also 'temas terminales' -> 'temas del terminal', and the one place that called
a layout a 'diseño' now says 'disposición', which is what app.layout says.

Separate commit on purpose: it is the one judgement call in this branch. Drop
it and the other two still stand.
@pdalexis
pdalexis force-pushed the add-settings-section-headings branch from b39e8f0 to 9592e07 Compare August 14, 2026 22:28
@pdalexis

pdalexis commented Aug 14, 2026

Copy link
Copy Markdown
Author

One thing I want to flag before you merge, because it is much cheaper to change now than later: I think two of the six key names I am adding are wrong in English, and if they go in as-is, fifteen files end up faithfully translating a word that does not describe what it labels.

form.identity

It groups category, title, host, port, username — it names which box you are connecting to. But it renders directly above form.auth, and in SSH "identity" already means IdentityFile, i.e. the private key. So the heading names the thing the next section actually contains.

You can see the trap in the translations I wrote for it. Russian is the clearest: Идентификация sitting on top of Авторизация reads as two headings for the same idea, with the wrong one holding host and port. French is nearly as bad, since that file already translates username as Identifiant — a section headed identité that is not about credentials is just confusing. Chinese and Japanese both drift toward "identifying information" in the personal-data sense.

connection, or general, would carry the meaning without colliding with auth.

setting.development

It groups disableDeveloperTool and debug — developer tooling, not the activity of developing software. Most files already have a good word for it sitting in disableDeveloperTool itself (Entwicklerwerkzeug, narzędzie deweloperskie, geliştirici aracını), and every translation I wrote ignored it and used the activity noun instead. Polish is the worst of them: Programowanie literally means "coding".

developer would match what the toggles do and what the existing strings already say.

The other four — start, privacy, onConnect, environment — I am happy with.

I have not changed anything. Renaming means touching common-fields.js and setting-common.jsx in electerm/electerm#4484 as well as all fifteen files here, so it is your call, and I would rather ask than churn the PR twice. Say the word and I will push both, or tell me to leave them and I will drop it without argument.


Separately, and also about these six headings rather than about the locale files: in electerm/electerm#4484 both .setting-section and .bookmark-form-section set text-transform: uppercase and letter-spacing: .05em. Two problems come out of that, and neither is fixable from this repo.

Turkish loses its dotted İ. CSS uppercasing follows the content language, and electerm never sets a lang attribute anywhere — I grepped the whole tree. So i uppercases to I instead of İ:

key renders should render
privacy GIZLILIK GİZLİLİK
development GELIŞTIRME GELİŞTİRME
identity KIMLIK KİMLİK
auth (already exists) YETKILENDIRME YETKİLENDİRME

start, onConnect and environment are unaffected — başlangıç and bağlanırken use the dotless ı, which is correct either way. Note auth is in that list: it is not a string this PR adds, it is just the first time an existing key gets rendered through an uppercasing rule, so the same fix covers both.

Arabic loses its joins. letter-spacing inserts space between letters that are supposed to connect cursively, so the Arabic headings render as disconnected glyphs.

Both belong in #4484, not here. If you want them fixed, I would set lang on the root element from the active locale — which is worth having regardless, since it also drives hyphenation and font fallback — and either drop letter-spacing or scope it to :not(:lang(ar)). Happy to push that to #4484; just did not want to widen this PR without asking.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant