Files
LambdaMail/services/webmail/messages/en.json
T
Lucas RayanandClaude Opus 5 ee240ae227 #23: show real data, unify the surfaces and make enrolment scannable
The interface was reporting things that were not true. Several screens were
mockups wired to nothing, and a reader with the console in Portuguese was
served a mixture of two languages.

Fabricated data, removed:

- The outbound queue was two invented jobs held in component state. Retry and
  Freeze only edited that local array, so the screen showed the same two rows
  on every deployment and reported success for actions that never happened.
  "Freeze" named an operation the backend has never implemented. It now reads
  /api/v1/admin/queue, which was already serving the real thing, and offers
  retry and cancel, which exist.
- Every dashboard KPI fell back to an invented constant (1420 received, 890
  sent, 3 queued, 18.4% disk), so an unreachable API produced a dashboard of
  plausible traffic for a server that might have delivered nothing. Two of
  those fields were never returned by the endpoint at all, so the fake disk
  figure was what everyone always saw. DMARC did the same with 1250/98%/97%.
- The domains screen showed thirteen DNS records for example.com, all marked
  VERIFIED, and posted {domain_id: "default-domain"} to endpoints expecting a
  real id. DKIM rotation sends "domain", not "domain_id", so its 400 was
  swallowed and a made-up TXT record displayed as though a key had been
  issued: rotation had never once worked from this screen.
- The TLS panel asserted a Let's Encrypt RSA 4096 certificate and verified
  DANE from static markup, on a deployment currently serving a self-signed
  one. It now reads the cert watcher via /api/v1/admin/tls.
- The readiness scorecard printed "10 / 10 all checks passed" regardless of
  what preflight reported, including failures.
- A message trace that found nothing invented a five-step delivery - TLS 1.3
  accepted, SPF pass, spam score 0.8 - for a message that does not exist.
- Both sidebars showed a fixed admin@lambdamail.local with a hardcoded
  "SUPER_ADMIN (2FA)", claiming a second factor whether or not one existed.
- Settings seeded a "[Boletim]" Sieve rule. Saving posts the whole list, so
  the first edit wrote that invented filter into the account's real script.

The reconcile endpoint itself answered {records_verified: 13, status:
"VERIFIED"} for any domain id, including one that does not exist, without
resolving a name. It now reports what it does, which is record the request.

Surfaces:

The root was a chooser listing webmail and the console side by side, which
asked visitors a question they should not have to answer and advertised cookie
names and audiences to anonymous readers. It now redirects into the mail
client. Accounts that may administer the server get a link to the console from
inside the app - a link to /admin/login, so crossing over still costs the
second factor the admin audience requires - and the console gets a plain link
back, since the webmail session was never given up to get there.

Enrolment:

Both enrolment screens printed the base32 secret as text and left the operator
to type 32 characters into their phone. The enrol endpoint already returned an
otpauth:// URI; it simply was not drawn. Added a shared component that renders
it as a QR locally - the seed is already in the page, and sending it to an
image service would put it in another request log for nothing.

The recovery-code step rendered its own "sign in" button while the form's
submit button stayed visible below it: two identical buttons, and no way to
copy the codes it insisted you save. It is now terminal, with copy and
continue.

Language:

translate() had no interpolation, so "Undo Send ({seconds}s remaining)" showed
the placeholder and a second hardcoded countdown was appended after it. It now
substitutes, leaving an unmatched token visible rather than blanking it.

The pt-BR bundle had lost its diacritics - "Destinatario", "Ultimo erro",
"Acoes" - from the ASCII-source rule being applied to the translations, which
are data. Restored, and the three bundles are back in key parity at 270 each,
with roughly 120 strings that were hardcoded in the source now keyed.

Healthchecks:

web had none, despite being the only service users reach directly: a wedged
Next process kept its container running and Traefik kept routing to it. Added,
using node's own fetch since the image ships no curl. rspamd and clamav gained
the timeouts and start_periods whose absence would fail them during startup.
migrate stays without one and says why: it is a one-shot job, and a probe
would report unhealthy exactly when it has succeeded.

Verified: 36 webmail tests and 41 auth tests against a real Postgres, both new
admin/me cases confirmed to fail without the route (404). lang-lint clean,
production build clean, 0 vulnerabilities, compose validates.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-02 19:13:53 -03:00

287 lines
11 KiB
JSON

{
"admin": {
"addAlias": "Add alias",
"aliasesTitle": "Aliases",
"auditLog": "Audit log",
"backToWebmail": "Back to webmail",
"cancelJob": "Cancel",
"certificateState": "Certificate",
"checksPassed": "{passed} of {total} passed",
"createAccount": "Create account",
"createAlias": "Create a routing alias",
"csvPreview": "Preview",
"dashboardSubtitle": "Live figures read from the database.",
"dashboardTitle": "Overview",
"dkimAlgorithm": "Key type",
"dkimOverlap": "Both keys sign outbound mail for {days} days so nothing fails while DNS propagates.",
"dkimPublishRecord": "Publish this record in DNS:",
"dkimRotate": "Generate a new key",
"dkimSelector": "Selector",
"dkimTitle": "DKIM key rotation",
"dmarcSubtitle": "Breakdown of the aggregate XML reports that have been ingested.",
"dmarcTitle": "DMARC reports",
"dnsRecordsFor": "DNS records for {domain}",
"dnsStatusDrift": "Drift",
"dnsStatusPartial": "Partial",
"dnsStatusVerified": "Verified",
"domainName": "Domain name",
"domainsTitle": "Domains",
"expiresInDays": "Expires in {days} days",
"freezeDomain": "Freeze domain",
"greylistScore": "Greylist score",
"headerScore": "Header-tagging score",
"jobCancelled": "Job {id} was cancelled.",
"jobRetried": "Job {id} was queued for another attempt.",
"lastChecked": "Last checked: {when}",
"lastReload": "Last reload",
"mailHost": "Mail host",
"mailboxCount": "{count} mailboxes",
"mailboxesSubtitle": "Accounts, aliases, CSV import and the second-factor policy.",
"mailboxesTitle": "Mailboxes",
"mfaAdmins": "Required for admins",
"mfaEveryone": "Required for everyone",
"mfaOptional": "Optional",
"mfaPolicyIntro": "Choose how strictly this domain requires a second factor.",
"mfaPolicyTitle": "Second-factor requirement",
"noCertificate": "No certificate is loaded for this host.",
"noChecks": "No checks reported.",
"noDomains": "No domains have been onboarded yet.",
"onboardIntro": "Creates the domain and its standard RFC 2142 aliases, pointed at your address.",
"onboardTitle": "Add a domain",
"openAdmin": "Admin console",
"preflightTitle": "Readiness checks",
"queueEmpty": "The outbound queue is empty.",
"queueSubtitle": "Inspect the outbound queue, retry a job, or cancel one.",
"queueTitle": "Outbound queue",
"readinessIntro": "Checks the server reports about itself before it carries production mail.",
"reconcileDns": "Reconcile DNS",
"reconcileNotImplemented": "Per-record DNS verification is not implemented yet: this records the request and re-reads the stored status, it does not query a resolver.",
"rejectScore": "Reject score",
"retryJob": "Retry",
"rspamdIntro": "Scores that trigger greylisting, header tagging and rejection.",
"rspamdSaved": "Thresholds saved.",
"saveThresholds": "Save thresholds",
"securitySubtitle": "Readiness checks, TLS state, spam thresholds and the audit trail.",
"securityTitle": "Security",
"tabAudit": "Audit trail",
"tabReadiness": "Readiness",
"tabRspamd": "Spam thresholds",
"tabTls": "TLS",
"tlsMode": "TLS mode",
"tlsTitle": "Certificate and transport",
"traceNoResults": "No trace found for that queue ID.",
"tracePlaceholder": "Queue ID",
"traceTitle": "Trace a message",
"unknownSni": "Unknown SNI requests",
"watcherHealthy": "Certificate watcher"
},
"auth": {
"adminLoginTitle": "Admin console",
"adminStepUpNote": "The admin console asks for your second factor again, even though you are already signed in to webmail.",
"authFailed": "Authentication failed",
"cantScanQr": "Cannot scan? Enter this key manually:",
"emailLabel": "Email address",
"enrolmentFailed": "Could not start the second-factor setup",
"enrolmentIntro": "The admin console requires a second factor. Set one up now to continue.",
"genericError": "Something went wrong",
"invalidCredentials": "Invalid email or password",
"mfaRequired": "Enter the six-digit code from your authenticator app.",
"passwordLabel": "Password",
"qrCodeAlt": "QR code for two-factor setup",
"scanQr": "Scan this code with Google Authenticator, Aegis, Authy or 1Password.",
"signInButton": "Sign in",
"signOutOfAdmin": "Leave admin",
"totpCodeLabel": "Authentication code",
"userLoginTitle": "Sign in to your mailbox",
"verificationFailed": "Verification failed",
"verifyCodeButton": "Verify code"
},
"common": {
"actions": "Actions",
"add": "Add",
"adminPortal": "Admin",
"appName": "LambdaMail",
"back": "Back",
"cancel": "Cancel",
"confirm": "Confirm",
"continue": "Continue",
"copied": "Copied",
"copy": "Copy",
"delete": "Delete",
"loading": "Loading...",
"logout": "Sign out",
"never": "Never",
"none": "None",
"refresh": "Refresh",
"retry": "Retry",
"save": "Save",
"search": "Search",
"status": "Status",
"unavailable": "Unavailable",
"userPortal": "Webmail"
},
"errors": {
"forbidden": "You do not have access to this",
"loadFailed": "Could not load the data",
"serverError": "Something went wrong on the server",
"unauthorized": "Please sign in again"
},
"mail": {
"addFiles": "Add files...",
"allMessages": "All",
"archive": "Archive",
"attachments": "Attachments ({count})",
"attachmentsFilter": "With attachments",
"attachmentsUnsupported": "Attachments are listed by name only; file upload is not wired up yet.",
"backToList": "Back to the message list",
"bold": "Bold",
"bulletList": "Bulleted list",
"compose": "Compose",
"discard": "Discard",
"draftSaved": "Draft saved automatically",
"drafts": "Drafts",
"forward": "Forward",
"hideCcBcc": "- Hide Cc/Bcc",
"inbox": "Inbox",
"insertLink": "Insert link",
"insertUrl": "Enter the URL:",
"italic": "Italic",
"junk": "Spam",
"loadImages": "Load images",
"noMessages": "No messages",
"numberedList": "Numbered list",
"reply": "Reply",
"sendMessage": "Send message",
"sending": "Sending...",
"sent": "Sent",
"showCcBcc": "+ Add Cc/Bcc",
"strikethrough": "Strikethrough",
"trash": "Trash",
"underline": "Underline",
"undoSend": "Sending in {seconds}s",
"undoSendAction": "Undo",
"unreadCount": "{count} unread",
"unreadFilter": "Unread"
},
"settings": {
"actionDiscard": "Discard",
"actionFlag": "Flag message",
"actionMove": "Move to Archive",
"addRule": "Add rule",
"appPasswordPlaceholder": "Thunderbird / Outlook Mobile",
"appPasswords": "App passwords",
"autoReplySaveFailed": "Could not save the auto-reply",
"enableMfa": "Enable two-factor",
"fieldFrom": "From",
"fieldHeader": "Header",
"fieldSubject": "Subject",
"generateAppPassword": "Generate",
"langEn": "English",
"langEs": "Español",
"langPtBR": "Português (Brasil)",
"language": "Language",
"matchContains": "contains",
"matchEquals": "is exactly",
"matchRegex": "matches regex",
"mfaDisabled": "Disabled",
"mfaEnabled": "Enabled",
"mfaStatus": "Two-factor status",
"recoveryCodesTitle": "Recovery codes",
"ruleIf": "IF",
"ruleThen": "THEN",
"saveAutoReply": "Save auto-reply",
"saveRecoveryCodes": "Save these somewhere safe. Each one works once, and they are shown only now.",
"security": "Security",
"sessions": "Sessions",
"settingsSaved": "Settings saved.",
"sieveIntro": "Rules that sort incoming mail automatically.",
"sieveTitle": "Filters (Sieve)",
"signOut": "Sign out",
"signatureIntro": "Appended automatically to the messages you compose.",
"signaturePlaceholder": "Kind regards,\nYour name\nYour role",
"signatureSaveFailed": "Could not save the signature",
"signatureSaved": "Signature saved.",
"signatureTitle": "Email signature",
"sixDigitCode": "Six-digit code",
"subtitle": "Two-factor authentication, filters, auto-reply and your signature.",
"title": "Settings",
"twoFactorIntro": "Protect your account with Google Authenticator, Aegis, Authy or 1Password.",
"vacationBodyLabel": "Reply message",
"vacationDefaultBody": "Thank you for your message. I am currently away and will reply when I return.",
"vacationDefaultSubject": "Out of office",
"vacationIntro": "Reply automatically to incoming mail while you are away.",
"vacationSubjectLabel": "Reply subject",
"vacationTitle": "Out-of-office auto-reply",
"valuePlaceholder": "Value to match..."
},
"ui": {
"accountStatus": "Account status",
"actions": "Actions",
"active": "Active",
"activeSessions": "Active sessions",
"actor": "Actor",
"actualRecord": "Actual value",
"allChecksPassed": "All checks passed",
"appPasswordGenerated": "App password generated",
"appPasswordsSection": "App passwords",
"attempts": "Attempts",
"bounceRate": "Bounce rate",
"changePassword": "Change password",
"clientIp": "Client IP",
"copyPasswordNow": "Copy it now. It is not shown again.",
"coreInfra": "Core services",
"currentPassword": "Current password",
"currentSession": "This device",
"daneVerified": "DANE verified",
"destination": "Destination",
"diskUsed": "Storage used",
"dkimAlignment": "DKIM alignment",
"dkimResult": "DKIM",
"dmarcEnforcement": "DMARC policy",
"domainVerification": "Domain verification",
"domainsVerified": "Domains verified",
"emailAddress": "Email address",
"eventAction": "Action",
"expectedValue": "Expected value",
"healthy": "Healthy",
"imapEngine": "IMAP service",
"inboundVolume": "Received (24h)",
"jobId": "Job",
"lastError": "Last error",
"liveMetrics": "Live metrics",
"localDisk": "Local disk",
"mailboxesActive": "Active mailboxes",
"messageBody": "Message",
"messageVolume": "Messages",
"newPassword": "New password",
"noAppPasswords": "No app passwords yet.",
"noData": "No data yet",
"online": "Online",
"outboundDelivered": "Sent (24h)",
"passwordChanged": "Password changed.",
"postgresRunner": "Postgres-backed queue",
"preflightTitle": "Readiness",
"provisionMailbox": "Create mailbox",
"queueDepth": "Queue depth",
"recipient": "Recipient",
"recordName": "Record",
"reportingOrg": "Reporting organisation",
"scanners": "Spam and virus scanning",
"sender": "Sender",
"signOutOthers": "Sign out other devices",
"smtpListener": "SMTP service",
"sourceIp": "Source IP",
"spfRatio": "SPF pass rate",
"spfResult": "SPF",
"storageQuota": "Storage quota",
"subject": "Subject",
"targetObject": "Target",
"timestamp": "Time",
"toRecipients": "To",
"toggleMenu": "Toggle menu",
"totalEvaluated": "Messages evaluated",
"twoFactorSection": "Two-factor authentication",
"watching": "Watching"
}
}