Skip to content

i18n: make missing Security and sign-in text translatable - #147

Draft
Thibault-MTZ wants to merge 9 commits into
X4Applegate:mainfrom
Thibault-MTZ:i18n-settings-security
Draft

Thibault-MTZ wants to merge 9 commits into
X4Applegate:mainfrom
Thibault-MTZ:i18n-settings-security

Conversation

@Thibault-MTZ

Copy link
Copy Markdown
Contributor

What & why

Following our discussion in #144, move the remaining hardcoded text in Security settings and sign-in onto the existing translation helper. This adds 122 English keys (104 Security, 18 login), with shared labels reused. French is prepared separately; this PR does not change fr.json or include the audit tool.

Three OIDC/TOTP help messages use named placeholders instead of sentence fragments, with their inline emphasis becoming plain text. Custom SSO button labels remain unchanged and escaped. Form values, CAPTCHA JavaScript, authentication, rate limits and session behavior are preserved.

I included sign-in after finding it still in English while testing. Happy to split it if you prefer a smaller first review.

Validation

Based on main e96a090, including v2.66.1. Go 1.27.2, Linux amd64, CGO disabled; tests use temporary databases and a fake Caddy API.

  • Full go test -p 1 -count=1 ./... and go build -p 1 ./... pass on both extraction and separate FR sources.
  • Security: locale fallback, form values, escaping, help interpolation and all six CAPTCHA configuration states, including secrets staying out of HTML.
  • Sign-in: site/browser locale selection, notices, SSO errors, invalid credentials, successful sessions, lockout and custom SSO labels.
  • Browser: Security EN/FR and sign-in FR at 1440/768/390/320 CSS px, panel switching and a real sign-in to the isolated preview. RU fallback also checked. These are viewport tests, not physical-device or cross-browser coverage.

Not tested: external OIDC/CAPTCHA services, real reset/invitation delivery, physical phones, screen readers or production-policy changes. Forgot/reset forms and TOTP verification are outside this batch. A long environment name can still overflow the unchanged shared header on mobile; the sign-in form fits.

Checklist / review

  • Tests, builds and formatting checks pass.
  • Translation documentation updated.
  • Changelog/version bump: left for agreement on the intended release number before this draft is marked ready.

@X4Applegate, please review the shape and test before merging. Sorry if my English is a little awkward; I am French and use translation tools to help. AI tools also helped with preparation; the checks and limits are listed above.

Exercise off, missing-key and configured states for both CAPTCHA providers,
plus the environment kill switch, in English, French, Russian and Chinese.
Assert that status labels resolve through the existing catalog fallback and
that configured secret keys are never rendered into the settings HTML.

Keep field names, provider values, JavaScript and security behavior unchanged.
This extends the focused Security extraction discussed in upstream X4Applegate#144;
French catalog changes remain on the dependent translation branch.

Validation: go test -p 1 -count=1 ./... and go build -p 1 ./... pass on both
extraction and French sources (Go 1.27.2, Linux amd64, CGO disabled).
Static review finds all 104 Security keys referenced and defined, matching
EN/FR placeholders, unchanged technical controls and unchanged CAPTCHA JS.
External CAPTCHA/OIDC providers and real policy changes are not covered.
Extend the Security extraction with the anonymous login page using 18 semantic English keys and the existing email label. Preserve site/browser locale precedence, generic credential failures, rate limits, sessions and form contracts. Translate the built-in SSO label while retaining and escaping administrator-defined labels.
Exercise browser/site language selection, reset/invite notices, SSO errors, invalid credentials, successful sessions and lockout. Check that custom SSO labels stay escaped and unchanged. The first SSO fixture lacked the required redirect URL; complete it and assert the configured button is actually rendered. Targeted localization/authentication tests and build pass on Go 1.27.2 Linux amd64.
@Thibault-MTZ Thibault-MTZ mentioned this pull request Oct 20, 2026
5 of 11 tasks

@X4Applegate X4Applegate left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed in detail. This is the right shape — approved. Mark it ready when you like and I'll merge.

First, your English is clear and the PR description is better than most; no need to apologise for it. Flagging the AI assistance and listing what you didn't test is exactly the right instinct, and it made this quicker to review rather than slower.

How I checked it

Rather than reading the diff and hoping, I built main and this branch, ran both, fetched /settings/security from each, stripped the markup and diffed the rendered English. That catches dropped sentences, altered meaning and broken interpolation in one pass.

Three differences, all accounted for:

  1. disabled → Disabled on the CAPTCHA status pill. You reused status.disabled, which is what I'd asked for — and the pill now matches every other status pill in the app, which were already capitalised. An improvement, not a regression.
  2. Two <strong> emphases become plain text — /callback, groups, and the user-menu path. Exactly what you disclosed.
  3. Port and temp-path differences — my test harness, not your change.

Nothing else moved. 219 lines of rendered text, no sentence lost.

On the lost emphasis — your call was right

I wrote the /callback line for #139 specifically because registering the bare /auth/oidc path is the most common OIDC mistake, so I did look hard at this one. Keeping inline markup would mean either splitting sentences into fragments — which produces bad translations, because word order differs by language — or letting catalogs carry HTML, which makes every translation a small injection surface. Complete sentences with named placeholders is the correct trade, and the warning that actually matters for safety — "Make sure your own IP is included before saving!" — kept its emphasis, with the key inside the <strong>. Good judgement.

The detail I was most pleased to see

// Translate only the built-in fallback, never an administrator's label.
if strings.TrimSpace(mustGetSetting(s.DB, settingOIDCButtonLabel)) == "" {
    data["OIDCButtonLabel"] = s.tr(r, "login.sso_default")
}

oidcConfig() already substitutes the default when the setting is empty, so translating oc.ButtonLabel unconditionally would have silently overwritten an admin's custom SSO label. You spotted that and checked the stored setting instead. That's the kind of thing a careless extraction gets wrong.

Everything else I verified

  • Fallback is clean. With 122 new English keys that fr, ru and zh-CN don't have, the page renders in French and Russian with zero raw keys leaking — English fills in, and the already-translated navigation stays translated.
  • No secrets in HTML. turnstile_secret_key, recaptcha_secret_key and oidc_client_secret still render with value="" on both branches. The placeholder swap didn't weaken that.
  • Form semantics untouched — every name, value, selected and {{if}} condition is identical; only display text moved.
  • No dead keys. I scanned all 122 against template and Go usage: every one is referenced. (Four looked orphaned until I noticed the SSO error keys go through a variable — they resolve correctly at runtime; I checked ?error=sso_nouser renders the right sentence.)
  • Full go test ./..., go vet, gofmt clean locally; CI green.

The docs/TRANSLATING.md addition is a genuinely good touch — naming {suffix}, {claim} and {profile} as placeholders to preserve is the thing translators most need told.

Your open question: the version

v2.67.0. Interface coverage expansions have taken a minor bump before — the v2.63.0 batch did — and this is the same kind of change.

Don't worry about writing the changelog entry; I'll add it on merge so it matches the house style. Just mark the PR ready.

On splitting

Including sign-in was the right call — it's small, it's adjacent, and you found it while testing the page it belongs to. No need to split.

For the next one, Settings → Notifications or DNS would be a natural follow-on, and the list pages after that. The proxy-host form stays last, as discussed.

@X4Applegate

Copy link
Copy Markdown
Owner

Small correction to one thing I told you, before you mark this ready.

I said this batch would be v2.67.0. That number has just been taken by an unrelated SSO change that shipped today, so this PR will be v2.68.0 instead. Nothing else changes — the review stands, the approval stands, and I'll still write the changelog entry on merge.

Apologies for the churn; that was my doing, not yours.

One heads-up while you're here. That release touched getLogin in internal/server/server.go — the same switch over sso_* errors your PR converts to keys. There's now a fourth case:

case "sso_domain":
    msg = "That account's email domain is not permitted to sign in here."

So you'll likely see a small conflict there when you rebase. Two options, whichever you prefer:

  • Rebase and fold it in — add a login.error.sso_domain key alongside the others and convert that case too. It's one more key in the same shape as the four you already have.
  • Leave it to me — I'll resolve it at merge and add the key myself, so you don't have to touch it.

Either is fine. If you'd rather not re-run your browser checks over a rebase, take the second option.

@X4Applegate

Copy link
Copy Markdown
Owner

Version update, and an apology for the churn — this is the second time I've moved your number, which is my fault, not yours.

v2.68.0 has now also gone, to a Users-page change that shipped today. Rather than reserve you a third number that might move again, let's drop the reservation: this PR takes whatever the next free version is when it merges. I'll fill it into the changelog myself at that point, so there's nothing for you to edit either way.

Reserving a number for an unmerged PR was a bad idea on my part. Nothing else changes — the approval stands, and the conflict note from earlier still applies:

  • getLogin in internal/server/server.go now has two extra sso_* cases rather than one (sso_domain from v2.67.0, and no change in v2.68.0 — but the earlier one is still there).
  • Same offer: rebase and fold them in, or leave it to me at merge so you don't have to re-run your browser checks.

No rush from this end.

This branch has not been deployed

No deployments
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.

2 participants