Say every Administration refusal in the reader's language #340

Closed
opened 2026-09-14 00:18:11 +00:00 by jcoffey-dev · 0 comments
Owner

Summary

Stalwart explains a refused change in English, and some of it reached the Administration pages as-is. Seen live: "Invalid domain name" for a reserved TLD and "Invalid email address" for a catch-all. The source also shows grant refusals, and ihasmail's own proxy messages were English too.

describeDirectoryError now never shows server text as-is:

  • Every error type the registry returns has its own translated message: invalidPatch, validationFailed, rateLimit, tooLarge, and a translated fallback that names only the error code.
  • Validator refusals are recognised by the validator's exact wording. The registry's string validators (crates/registry/src/types/string.rs) are the whole list: domain name, email address, local part, host name, empty value. Each is explained again, with how to fix it.
  • forbidden has specific messages: a permission you can't grant, an account that signs in through an external directory, and a licence account limit.
  • ihasmail's own refusals (administration_needs_own_device, administration_disabled, network, rate limit) are explained by code, not by their English message.
  • Domain wording where it differs: a duplicate domain name and a missing domain no longer read as "address in use" and "account no longer exists". Domain callers pass "domain".
  • The one deliberate exception: a password policy's reason still follows a translated sentence. The rule is the server's and can't be known in advance to translate, and dropping it would leave no way to find out why.

Mock. It now refuses a reserved TLD and a catch-all without a domain with the live server's invalidPatch and wording, so this path can be exercised in dev:mock.

KNOWN-ISSUES.md

  • The untranslated-refusals entry now covers only the password-policy exception.
  • The last two live cases (signing in with an administrator-set password, and the read-only guard) are recorded as tried on the live server and holding.

Testing

  • New tests:
    • validator descriptions translated and not echoed;
    • unknown descriptions never echoed, including for forbidden and unknown types;
    • grant and external-directory refusals;
    • domain versus account wording;
    • ihasmail's own refusal codes;
    • a method-level error reduced to its code;
    • the mock refusing like the live validators.
  • Checks:
    • npm run typecheck: clean;
    • npm test -w web: 1215/1215;
    • npm test -w server: all pass;
    • npm run build: succeeds.
  • Not driven in the browser: the change is to the message text only, and the panels already show whatever this function returns. That was checked earlier with the password refusal.

Translations

15 new keys in all nine catalogues. 3 retired: the messages that wrapped Stalwart's English (The mail server refused this: {reason}, The mail server rejected a value: {reason}, The mail server rejected a value.). Strings falling back to English: 16 before, 16 after, and no stale keys.

Merged 2026-09-13 as coffey-labs/ihasmail@5f808f3033

Rebuilt from: git history, session transcript.

## Summary Stalwart explains a refused change in English, and some of it reached the Administration pages as-is. Seen live: "Invalid domain name" for a reserved TLD and "Invalid email address" for a catch-all. The source also shows grant refusals, and ihasmail's own proxy messages were English too. `describeDirectoryError` now never shows server text as-is: - **Every error type the registry returns has its own translated message:** `invalidPatch`, `validationFailed`, `rateLimit`, `tooLarge`, and a translated fallback that names only the error code. - **Validator refusals are recognised by the validator's exact wording.** The registry's string validators (`crates/registry/src/types/string.rs`) are the whole list: domain name, email address, local part, host name, empty value. Each is explained again, with how to fix it. - **`forbidden` has specific messages:** a permission you can't grant, an account that signs in through an external directory, and a licence account limit. - **ihasmail's own refusals** (`administration_needs_own_device`, `administration_disabled`, network, rate limit) are explained by code, not by their English message. - **Domain wording where it differs:** a duplicate domain name and a missing domain no longer read as "address in use" and "account no longer exists". Domain callers pass `"domain"`. - **The one deliberate exception:** a password policy's reason still follows a translated sentence. The rule is the server's and can't be known in advance to translate, and dropping it would leave no way to find out why. **Mock.** It now refuses a reserved TLD and a catch-all without a domain with the live server's `invalidPatch` and wording, so this path can be exercised in `dev:mock`. **KNOWN-ISSUES.md** - The untranslated-refusals entry now covers only the password-policy exception. - The last two live cases (signing in with an administrator-set password, and the read-only guard) are recorded as tried on the live server and holding. ## Testing - **New tests:** - validator descriptions translated and not echoed; - unknown descriptions never echoed, including for `forbidden` and unknown types; - grant and external-directory refusals; - domain versus account wording; - ihasmail's own refusal codes; - a method-level error reduced to its code; - the mock refusing like the live validators. - **Checks:** - `npm run typecheck`: clean; - `npm test -w web`: 1215/1215; - `npm test -w server`: all pass; - `npm run build`: succeeds. - **Not driven in the browser:** the change is to the message text only, and the panels already show whatever this function returns. That was checked earlier with the password refusal. ## Translations **15 new keys** in all nine catalogues. **3 retired:** the messages that wrapped Stalwart's English (`The mail server refused this: {reason}`, `The mail server rejected a value: {reason}`, `The mail server rejected a value.`). Strings falling back to English: **16 before, 16 after**, and no stale keys. **Merged** 2026-09-13 as coffey-labs/ihasmail@5f808f303373 <sub>Rebuilt from: git history, session transcript.</sub>
This repo is archived. You cannot comment on issues.