Four faults, all in the path between the compose screen and the mailbox.
**Every message sent from the webmail had an empty body.** The HTTP handler
read a field named "body"; the composer has always sent "html". The two never
matched, so the payload handed to the queue contained headers and nothing
else. The handler now reads html and text, and still accepts body so an older
client is not broken. Sending was otherwise working, which is why this went
unnoticed: the mail left, it just said nothing.
**Bcc was discarded.** The field was absent from the request struct and from
ComposeInput, so a blind copy typed into the webmail reached nobody. It now
joins the envelope recipients and is deliberately kept out of the headers,
which is what makes it blind.
**HTML was labelled text/plain.** Even once the body was carried, the composer
produces markup and the message declared text/plain, so a recipient would see
raw tags. Messages with HTML now go out as multipart/alternative with a
plain-text part derived from the markup and ordered first per RFC 2046 5.1.4.
An empty or missing text alternative is itself a spam signal.
**Sent mail left no trace.** Nothing wrote to the Sent folder, so a delivered
message vanished from the sender's view entirely. A copy is now filed after
the message is durably queued - not before, or a send refused by the rate
limit would still leave a copy - reusing the inbound persistence path so it
gets a real IMAP UID and appears over IMAP too. A failure to file is logged
and swallowed: the mail is already on its way, and reporting failure would
invite the user to send it twice.
**Drafts were never saved.** There was no endpoint at all. The composer showed
"draft saved automatically" from a setTimeout with no request behind it, so
closing the tab lost the message the screen had just promised was safe. Adds
POST /api/v1/mail/draft, storing the draft as a whole RFC 5322 message in the
Drafts folder, and debounces the composer onto it.
The composer also navigated to Sent whether or not the send succeeded, which
is how a rejected message disappears silently. It now reports the error and
stays put.
**Delay notifications were being filed as spam.** A DSN this server writes is
injected through the same inbound path as mail from the internet, but without
a client IP - so SPF can only be "none" - and its bounce-shaped content scores
highly. The warning telling you your mail was delayed landed in your own Junk
folder. Messages the server generates itself now skip content scanning; the
exemption is narrow, and a test covers that ordinary mail is still scanned so
this cannot become a way past the filter. The DSN also declared
Auto-Submitted: auto-replied, which belongs to vacation responders; a delivery
report is auto-generated (RFC 3834 section 5), and filters read that header.
Not fixed here, because it is not a code fault: outbound port 25 is blocked by
the provider, which is what the delay notification was reporting
("i/o timeout" to Google's MX - a timeout, not a refusal, is the signature of
a block). Receiving is unaffected and queued mail keeps retrying. Documented
with the two ways out, including the relay path, which is verified working
code rather than another stub.
Verified: 22 Go packages pass, 5 new compose tests and 3 for the DSN handling.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
289 lines
12 KiB
JSON
289 lines
12 KiB
JSON
{
|
|
"admin": {
|
|
"addAlias": "Adicionar apelido",
|
|
"aliasesTitle": "Apelidos",
|
|
"auditLog": "Trilha de auditoria",
|
|
"backToWebmail": "Voltar ao webmail",
|
|
"cancelJob": "Cancelar",
|
|
"certificateState": "Certificado",
|
|
"checksPassed": "{passed} de {total} aprovadas",
|
|
"createAccount": "Criar conta",
|
|
"createAlias": "Criar um apelido de roteamento",
|
|
"csvPreview": "Pré-visualizar",
|
|
"dashboardSubtitle": "Números ao vivo lidos do banco de dados.",
|
|
"dashboardTitle": "Visão geral",
|
|
"dkimAlgorithm": "Tipo de chave",
|
|
"dkimOverlap": "As duas chaves assinam as mensagens enviadas por {days} dias, para nada falhar enquanto o DNS propaga.",
|
|
"dkimPublishRecord": "Publique este registro no DNS:",
|
|
"dkimRotate": "Gerar uma nova chave",
|
|
"dkimSelector": "Seletor",
|
|
"dkimTitle": "Rotação de chave DKIM",
|
|
"dmarcSubtitle": "Detalhamento dos relatórios XML agregados que foram processados.",
|
|
"dmarcTitle": "Relatórios DMARC",
|
|
"dnsRecordsFor": "Registros DNS de {domain}",
|
|
"dnsStatusDrift": "Divergente",
|
|
"dnsStatusPartial": "Parcial",
|
|
"dnsStatusVerified": "Verificado",
|
|
"domainName": "Nome do domínio",
|
|
"domainsTitle": "Domínios",
|
|
"expiresInDays": "Expira em {days} dias",
|
|
"freezeDomain": "Congelar domínio",
|
|
"greylistScore": "Pontuação de greylist",
|
|
"headerScore": "Pontuação de marcação de cabeçalho",
|
|
"jobCancelled": "O trabalho {id} foi cancelado.",
|
|
"jobRetried": "O trabalho {id} foi recolocado na fila para nova tentativa.",
|
|
"lastChecked": "Última verificação: {when}",
|
|
"lastReload": "Última recarga",
|
|
"mailHost": "Host de e-mail",
|
|
"mailboxCount": "{count} caixas de e-mail",
|
|
"mailboxesSubtitle": "Contas, apelidos, importação CSV e a política de segundo fator.",
|
|
"mailboxesTitle": "Caixas de e-mail",
|
|
"mfaAdmins": "Obrigatório para administradores",
|
|
"mfaEveryone": "Obrigatório para todos",
|
|
"mfaOptional": "Opcional",
|
|
"mfaPolicyIntro": "Escolha com que rigor este domínio exige um segundo fator.",
|
|
"mfaPolicyTitle": "Exigência de segundo fator",
|
|
"noCertificate": "Nenhum certificado carregado para este host.",
|
|
"noChecks": "Nenhuma verificação relatada.",
|
|
"noDomains": "Nenhum domínio foi cadastrado ainda.",
|
|
"onboardIntro": "Cria o domínio e os apelidos padrão da RFC 2142, apontados para o seu endereço.",
|
|
"onboardTitle": "Adicionar um domínio",
|
|
"openAdmin": "Console de administração",
|
|
"preflightTitle": "Verificações de prontidão",
|
|
"queueEmpty": "A fila de saída está vazia.",
|
|
"queueSubtitle": "Inspecione a fila de saída, tente reenviar um trabalho ou cancele um.",
|
|
"queueTitle": "Fila de saída",
|
|
"readinessIntro": "Verificações que o servidor relata sobre si mesmo antes de transportar mensagens em produção.",
|
|
"reconcileDns": "Reconciliar DNS",
|
|
"reconcileNotImplemented": "A verificação de registros DNS individuais ainda não foi implementada: isto registra a solicitação e relê o status armazenado, não consulta um resolvedor.",
|
|
"rejectScore": "Pontuação de rejeição",
|
|
"retryJob": "Tentar novamente",
|
|
"rspamdIntro": "Pontuações que acionam greylisting, marcação de cabeçalho e rejeição.",
|
|
"rspamdSaved": "Limites salvos.",
|
|
"saveThresholds": "Salvar limites",
|
|
"securitySubtitle": "Verificações de prontidão, estado do TLS, limites de spam e a trilha de auditoria.",
|
|
"securityTitle": "Segurança",
|
|
"tabAudit": "Trilha de auditoria",
|
|
"tabReadiness": "Prontidão",
|
|
"tabRspamd": "Limites de spam",
|
|
"tabTls": "TLS",
|
|
"tlsMode": "Modo TLS",
|
|
"tlsTitle": "Certificado e transporte",
|
|
"traceNoResults": "Nenhum rastro encontrado para esse ID de fila.",
|
|
"tracePlaceholder": "ID da fila",
|
|
"traceTitle": "Rastrear uma mensagem",
|
|
"unknownSni": "Requisições SNI desconhecidas",
|
|
"watcherHealthy": "Monitor de certificado"
|
|
},
|
|
"auth": {
|
|
"adminLoginTitle": "Console de administração",
|
|
"adminStepUpNote": "O console de administração pede o segundo fator novamente, mesmo você já estando conectado ao webmail.",
|
|
"authFailed": "Falha na autenticação",
|
|
"cantScanQr": "Não consegue escanear? Digite esta chave manualmente:",
|
|
"emailLabel": "Endereço de e-mail",
|
|
"enrolmentFailed": "Não foi possível iniciar a configuração do segundo fator",
|
|
"enrolmentIntro": "O console de administração exige um segundo fator. Configure um agora para continuar.",
|
|
"genericError": "Algo deu errado",
|
|
"invalidCredentials": "E-mail ou senha inválidos",
|
|
"mfaRequired": "Digite o código de seis dígitos do seu aplicativo autenticador.",
|
|
"passwordLabel": "Senha",
|
|
"qrCodeAlt": "Código QR para configurar a verificação em duas etapas",
|
|
"scanQr": "Escaneie este código com o Google Authenticator, Aegis, Authy ou 1Password.",
|
|
"signInButton": "Entrar",
|
|
"signOutOfAdmin": "Sair da administração",
|
|
"totpCodeLabel": "Código de autenticação",
|
|
"userLoginTitle": "Entre na sua caixa de e-mail",
|
|
"verificationFailed": "Falha na verificação",
|
|
"verifyCodeButton": "Verificar código"
|
|
},
|
|
"common": {
|
|
"actions": "Ações",
|
|
"add": "Adicionar",
|
|
"adminPortal": "Administração",
|
|
"appName": "LambdaMail",
|
|
"back": "Voltar",
|
|
"cancel": "Cancelar",
|
|
"confirm": "Confirmar",
|
|
"continue": "Continuar",
|
|
"copied": "Copiado",
|
|
"copy": "Copiar",
|
|
"delete": "Excluir",
|
|
"loading": "Carregando...",
|
|
"logout": "Sair",
|
|
"never": "Nunca",
|
|
"none": "Nenhum",
|
|
"refresh": "Atualizar",
|
|
"retry": "Tentar novamente",
|
|
"save": "Salvar",
|
|
"search": "Buscar",
|
|
"status": "Status",
|
|
"unavailable": "Indisponível",
|
|
"userPortal": "Webmail"
|
|
},
|
|
"errors": {
|
|
"forbidden": "Você não tem acesso a isto",
|
|
"loadFailed": "Não foi possível carregar os dados",
|
|
"serverError": "Algo deu errado no servidor",
|
|
"unauthorized": "Entre novamente"
|
|
},
|
|
"mail": {
|
|
"addFiles": "Adicionar arquivos...",
|
|
"allMessages": "Todas",
|
|
"archive": "Arquivo",
|
|
"attachments": "Anexos ({count})",
|
|
"attachmentsFilter": "Com anexos",
|
|
"attachmentsUnsupported": "Anexos são listados apenas pelo nome; o envio de arquivos ainda não está implementado.",
|
|
"backToList": "Voltar para a lista de mensagens",
|
|
"bold": "Negrito",
|
|
"bulletList": "Lista com marcadores",
|
|
"compose": "Escrever",
|
|
"discard": "Descartar",
|
|
"draftSaveFailed": "Não foi possível salvar o rascunho",
|
|
"draftSaved": "Rascunho salvo automaticamente",
|
|
"draftSaving": "Salvando rascunho...",
|
|
"drafts": "Rascunhos",
|
|
"forward": "Encaminhar",
|
|
"hideCcBcc": "- Ocultar Cc/Cco",
|
|
"inbox": "Caixa de entrada",
|
|
"insertLink": "Inserir link",
|
|
"insertUrl": "Digite a URL:",
|
|
"italic": "Itálico",
|
|
"junk": "Spam",
|
|
"loadImages": "Carregar imagens",
|
|
"noMessages": "Nenhuma mensagem",
|
|
"numberedList": "Lista numerada",
|
|
"reply": "Responder",
|
|
"sendMessage": "Enviar mensagem",
|
|
"sending": "Enviando...",
|
|
"sent": "Enviados",
|
|
"showCcBcc": "+ Adicionar Cc/Cco",
|
|
"strikethrough": "Tachado",
|
|
"trash": "Lixeira",
|
|
"underline": "Sublinhado",
|
|
"undoSend": "Enviando em {seconds}s",
|
|
"undoSendAction": "Desfazer",
|
|
"unreadCount": "{count} não lidas",
|
|
"unreadFilter": "Não lidas"
|
|
},
|
|
"settings": {
|
|
"actionDiscard": "Descartar",
|
|
"actionFlag": "Marcar mensagem",
|
|
"actionMove": "Mover para o Arquivo",
|
|
"addRule": "Adicionar regra",
|
|
"appPasswordPlaceholder": "Thunderbird / Outlook Mobile",
|
|
"appPasswords": "Senhas de aplicativo",
|
|
"autoReplySaveFailed": "Não foi possível salvar a resposta automática",
|
|
"enableMfa": "Ativar verificação em duas etapas",
|
|
"fieldFrom": "De",
|
|
"fieldHeader": "Cabeçalho",
|
|
"fieldSubject": "Assunto",
|
|
"generateAppPassword": "Gerar",
|
|
"langEn": "English",
|
|
"langEs": "Español",
|
|
"langPtBR": "Português (Brasil)",
|
|
"language": "Idioma",
|
|
"matchContains": "contém",
|
|
"matchEquals": "é exatamente",
|
|
"matchRegex": "corresponde à regex",
|
|
"mfaDisabled": "Desativada",
|
|
"mfaEnabled": "Ativada",
|
|
"mfaStatus": "Status da verificação em duas etapas",
|
|
"recoveryCodesTitle": "Códigos de recuperação",
|
|
"ruleIf": "SE",
|
|
"ruleThen": "ENTÃO",
|
|
"saveAutoReply": "Salvar resposta automática",
|
|
"saveRecoveryCodes": "Guarde-os em local seguro. Cada um funciona uma única vez e eles só aparecem agora.",
|
|
"security": "Segurança",
|
|
"sessions": "Sessões",
|
|
"settingsSaved": "Configurações salvas.",
|
|
"sieveIntro": "Regras que organizam as mensagens recebidas automaticamente.",
|
|
"sieveTitle": "Filtros (Sieve)",
|
|
"signOut": "Sair",
|
|
"signatureIntro": "Anexada automaticamente às mensagens que você escreve.",
|
|
"signaturePlaceholder": "Atenciosamente,\nSeu nome\nSeu cargo",
|
|
"signatureSaveFailed": "Não foi possível salvar a assinatura",
|
|
"signatureSaved": "Assinatura salva.",
|
|
"signatureTitle": "Assinatura de e-mail",
|
|
"sixDigitCode": "Código de seis dígitos",
|
|
"subtitle": "Verificação em duas etapas, filtros, resposta automática e sua assinatura.",
|
|
"title": "Configurações",
|
|
"twoFactorIntro": "Proteja sua conta com o Google Authenticator, Aegis, Authy ou 1Password.",
|
|
"vacationBodyLabel": "Mensagem da resposta",
|
|
"vacationDefaultBody": "Obrigado pela sua mensagem. Estou ausente no momento e responderei assim que retornar.",
|
|
"vacationDefaultSubject": "Fora do escritório",
|
|
"vacationIntro": "Responde automaticamente às mensagens recebidas enquanto você estiver ausente.",
|
|
"vacationSubjectLabel": "Assunto da resposta",
|
|
"vacationTitle": "Resposta automática de ausência",
|
|
"valuePlaceholder": "Valor a corresponder..."
|
|
},
|
|
"ui": {
|
|
"accountStatus": "Status da conta",
|
|
"actions": "Ações",
|
|
"active": "Ativo",
|
|
"activeSessions": "Sessões ativas",
|
|
"actor": "Autor",
|
|
"actualRecord": "Valor atual",
|
|
"allChecksPassed": "Todas as verificações passaram",
|
|
"appPasswordGenerated": "Senha de aplicativo gerada",
|
|
"appPasswordsSection": "Senhas de aplicativo",
|
|
"attempts": "Tentativas",
|
|
"bounceRate": "Taxa de devolução",
|
|
"changePassword": "Alterar senha",
|
|
"clientIp": "IP do cliente",
|
|
"copyPasswordNow": "Copie agora. Ela não será exibida novamente.",
|
|
"coreInfra": "Serviços principais",
|
|
"currentPassword": "Senha atual",
|
|
"currentSession": "Este dispositivo",
|
|
"daneVerified": "DANE verificado",
|
|
"destination": "Destino",
|
|
"diskUsed": "Armazenamento usado",
|
|
"dkimAlignment": "Alinhamento DKIM",
|
|
"dkimResult": "DKIM",
|
|
"dmarcEnforcement": "Política DMARC",
|
|
"domainVerification": "Verificação de domínio",
|
|
"domainsVerified": "Domínios verificados",
|
|
"emailAddress": "Endereço de e-mail",
|
|
"eventAction": "Ação",
|
|
"expectedValue": "Valor esperado",
|
|
"healthy": "Saudável",
|
|
"imapEngine": "Serviço IMAP",
|
|
"inboundVolume": "Recebidas (24h)",
|
|
"jobId": "Trabalho",
|
|
"lastError": "Último erro",
|
|
"liveMetrics": "Métricas ao vivo",
|
|
"localDisk": "Disco local",
|
|
"mailboxesActive": "Caixas ativas",
|
|
"messageBody": "Mensagem",
|
|
"messageVolume": "Mensagens",
|
|
"newPassword": "Nova senha",
|
|
"noAppPasswords": "Nenhuma senha de aplicativo ainda.",
|
|
"noData": "Ainda sem dados",
|
|
"online": "Online",
|
|
"outboundDelivered": "Enviadas (24h)",
|
|
"passwordChanged": "Senha alterada.",
|
|
"postgresRunner": "Fila baseada em Postgres",
|
|
"preflightTitle": "Prontidão",
|
|
"provisionMailbox": "Criar caixa de e-mail",
|
|
"queueDepth": "Tamanho da fila",
|
|
"recipient": "Destinatário",
|
|
"recordName": "Registro",
|
|
"reportingOrg": "Organização que reportou",
|
|
"scanners": "Análise de spam e vírus",
|
|
"sender": "Remetente",
|
|
"signOutOthers": "Sair dos outros dispositivos",
|
|
"smtpListener": "Serviço SMTP",
|
|
"sourceIp": "IP de origem",
|
|
"spfRatio": "Taxa de aprovação SPF",
|
|
"spfResult": "SPF",
|
|
"storageQuota": "Cota de armazenamento",
|
|
"subject": "Assunto",
|
|
"targetObject": "Alvo",
|
|
"timestamp": "Horário",
|
|
"toRecipients": "Para",
|
|
"toggleMenu": "Alternar menu",
|
|
"totalEvaluated": "Mensagens avaliadas",
|
|
"twoFactorSection": "Verificação em duas etapas",
|
|
"watching": "Monitorando"
|
|
}
|
|
}
|