From f5ba09ef7745c4004ab9eddb02daceb47504315f Mon Sep 17 00:00:00 2001 From: John Coffey Date: Thu, 3 Sep 2026 11:46:28 -0700 Subject: [PATCH 1/2] Catch the Brazilian Portuguese catalogue up with what shipped after it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Features landed after the catalogues were written and their strings were never added, so they rendered in English. Reported against German (#247); every language had the identical gap. 176 entries: 153 from features that shipped after the translation pass, and 23 keyboard bindings whose group and description are registered in English at the call site and translated at render. Follows the decisions this file already pins: você rather than the formal address the other Phase 1 languages took, and the fixed terminology, so Caixa de entrada, Pasta, Mensagem, Conversa, Marcador, Lixeira, Rascunhos and Configurações read the same here as everywhere above. Fifteen strings are deliberately absent and keep falling back to English: example.com and the other input placeholders, the product names, and the verbatim header names List-Id and X-Spam-Status. Verified: typecheck clean, 1000 tests pass, nothing missing from pt-BR.ts. --- web/src/locales/pt-BR.ts | 183 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+) diff --git a/web/src/locales/pt-BR.ts b/web/src/locales/pt-BR.ts index 30d8163..3366577 100644 --- a/web/src/locales/pt-BR.ts +++ b/web/src/locales/pt-BR.ts @@ -1054,6 +1054,189 @@ export const catalog: Catalog = { "Draft saved": "Rascunho salvo", "Emptying folder…": "Esvaziando a pasta…", "Nothing unread here": "Não há nada não lido aqui", + // ── Added after the first translation pass ────────────────────────── + // Features that shipped after the catalogues were written, so these + // strings had no entry here and fell back to English. Reported by a + // native speaker reviewing the German catalogue (#247); every language + // had the same gap. The keyboard bindings among them register their + // group and description in English at the call site and are translated + // at render. + " and {count} more": " e mais {count}", + "10 people or more": "10 pessoas ou mais", + "20 people or more": "20 pessoas ou mais", + "5 people or more": "5 pessoas ou mais", + "50 people or more": "50 pessoas ou mais", + "A banner on any message whose sender is not on one of your own domains.": "Um aviso em qualquer mensagem cujo remetente não esteja em um dos seus próprios domínios.", + "A calendar of its own, derived from the birthdays already on your contact cards. Nothing is written anywhere — the dates stay on the cards, and an event disappears when the contact does or the birthday is cleared. It can be hidden from the calendar’s own sidebar without turning it off here.": "Uma agenda própria, derivada dos aniversários que já estão nos seus cartões de contato. Nada é gravado em lugar nenhum: as datas continuam nos cartões, e um evento desaparece quando o contato desaparece ou o aniversário é apagado. Ela pode ser ocultada na barra lateral da agenda sem ser desativada aqui.", + "A calendar published at a URL — a timetable, a rota, a public holiday list. It is read-only, refreshed when you open the calendar, and never stored: the events are fetched and kept only for as long as this tab is open.": "Uma agenda publicada em uma URL: um horário, uma escala, uma lista de feriados. É somente leitura, atualizada quando você abre a agenda e nunca armazenada: os eventos são buscados e mantidos apenas enquanto esta aba estiver aberta.", + "A link whose text names one domain and whose destination is another is always flagged, even where the destination is trusted — being trusted is not the same as being the place the text claimed.": "Um link cujo texto nomeia um domínio e cujo destino é outro é sempre sinalizado, mesmo quando o destino é confiável: ser confiável não é o mesmo que ser o lugar que o texto anunciava.", + "Actions": "Ações", + "Added from a message, and removable here — previously the only way to undo one was to find another message from the same sender.": "Adicionado a partir de uma mensagem e removível aqui: antes, a única forma de desfazer era encontrar outra mensagem do mesmo remetente.", + "Added here, or from the dialog when a link is opened. A domain also covers its subdomains.": "Adicionado aqui ou pela caixa de diálogo ao abrir um link. Um domínio também abrange os seus subdomínios.", + "Agenda view": "Visualização de agenda", + "All three start switched off. A client that begins by interrupting is one people learn to click through, and a warning clicked through without reading costs the same attention and buys nothing.": "Os três começam desligados. Um cliente que começa interrompendo é um cliente que as pessoas aprendem a dispensar sem ler, e um aviso dispensado sem leitura custa a mesma atenção e não traz nada.", + "All {n} in {folder} are selected.": "Todas as {n} em {folder} estão selecionadas.", + "All {n} on this page are selected.": "Todas as {n} desta página estão selecionadas.", + "Also count these domains as inside": "Contar também estes domínios como internos", + "Always": "Sempre", + "Always showing images from": "Sempre exibindo imagens de", + "An image loaded from a sender's server tells them the message was opened, when, and from roughly where. Approved images are fetched by ihasmail's own server rather than the browser, so the sender learns none of those.": "Uma imagem carregada do servidor do remetente informa a ele que a mensagem foi aberta, quando e aproximadamente de onde. As imagens aprovadas são buscadas pelo próprio servidor do ihasmail, e não pelo navegador, de modo que o remetente não fica sabendo de nada disso.", + "Applies to": "Aplica-se a", + "Archive and next": "Arquivar e próxima", + "Archive by month": "Arquivar por mês", + "Archive by year": "Arquivar por ano", + "Archive failed: {error}": "Falha ao arquivar: {error}", + "Archive to {folder}": "Arquivar em {folder}", + "Ask before opening a link in a message": "Perguntar antes de abrir um link em uma mensagem", + "Ask before sending outside": "Perguntar antes de enviar para fora", + "Ask before sending to a large group": "Perguntar antes de enviar para um grupo grande", + "Back to the event": "Voltar ao evento", + "Before it happens": "Antes que aconteça", + "Birthdays": "Aniversários", + "By sender": "Por remetente", + "By subject": "Por assunto", + "Choose…": "Escolher…", + "Classic": "Clássico", + "Click to move the event": "Clique para mover o evento", + "Close without saving?": "Fechar sem salvar?", + "Compose as new": "Escrever como nova mensagem", + "Compose new message": "Escrever nova mensagem", + "Conversation": "Conversa", + "Conversation moved to {folder}": "Conversa movida para {folder}", + "Could not be read": "Não foi possível ler", + "Could not import this file: {error}": "Não foi possível importar este arquivo: {error}", + "Could not load this file.": "Não foi possível carregar este arquivo.", + "Could not read this calendar: {reason}": "Não foi possível ler esta agenda: {reason}", + "Could not read winmail.dat. The original is still attached below.": "Não foi possível ler o winmail.dat. O original continua anexado abaixo.", + "Counts people rather than headers, so one address in To and nine in Cc is a message to ten. Catches a reply-all onto a long thread.": "Conta pessoas em vez de cabeçalhos, então um endereço em Para e nove em Cc é uma mensagem para dez. Detecta um responder a todos em uma conversa longa.", + "Date received": "Data de recebimento", + "Date sent": "Data de envio", + "Day view": "Visualização de dia", + "Dracula, Gruvbox, Rosé Pine and Tokyo Night are the work of their own projects and are used under the MIT licence; the shades between their published colours are derived, and every one of them is checked for contrast. The accent colour below still applies over any of them.": "Dracula, Gruvbox, Rosé Pine e Tokyo Night são obra dos seus próprios projetos e são usados sob a licença MIT; os tons entre as cores publicadas por eles são derivados, e cada um deles é verificado quanto ao contraste. A cor de destaque abaixo continua se aplicando sobre qualquer um deles.", + "Earlier": "Antes", + "Every folder": "Todas as pastas", + "Everyone addressed will receive this.": "Todos os destinatários receberão isto.", + "File contents": "Conteúdo do arquivo", + "Filled in when the template is inserted, so you can edit the result before sending. One that cannot be answered yet — a recipient's name on a message you have not addressed — is left in the body as written, rather than becoming a blank.": "Preenchido quando o modelo é inserido, para que você possa editar o resultado antes de enviar. Um campo que ainda não pode ser resolvido — o nome de um destinatário em uma mensagem que você ainda não endereçou — permanece no corpo como foi escrito, em vez de virar um espaço em branco.", + "Forward as attachment": "Encaminhar como anexo", + "From the birthdays on your contacts. Nothing is stored.": "A partir dos aniversários dos seus contatos. Nada é armazenado.", + "Go to Calendar": "Ir para a Agenda", + "Go to Contacts": "Ir para Contatos", + "Go to Drafts": "Ir para Rascunhos", + "Go to Files": "Ir para Arquivos", + "Go to Inbox": "Ir para a Caixa de entrada", + "Go to Sent": "Ir para Enviados", + "Go to Settings": "Ir para Configurações", + "Go to Starred": "Ir para Favoritos", + "Import iCAL file…": "Importar arquivo iCAL…", + "Labels are IMAP keywords stored on your messages, so every other client sees them. Names, colours and nesting are ihasmail’s own and follow your account. Nesting is display only — it rewrites nothing in the mailbox.": "Os marcadores são palavras-chave IMAP armazenadas nas suas mensagens, então todos os outros clientes os veem. Os nomes, as cores e o aninhamento são do próprio ihasmail e acompanham a sua conta. O aninhamento é apenas de exibição: não reescreve nada na caixa postal.", + "Largest first": "Maiores primeiro", + "Later": "Depois", + "Light or dark": "Claro ou escuro", + "Mark messages from outside": "Marcar mensagens vindas de fora", + "Marked as spam": "Marcada como spam", + "Message order": "Ordem das mensagens", + "Month view": "Visualização de mês", + "More ways to send this": "Outras formas de enviar isto", + "Names the outside recipients and asks, rather than refusing.": "Nomeia os destinatários externos e pergunta, em vez de recusar.", + "Navigation": "Navegação", + "Nested under": "Aninhado em", + "Never": "Nunca", + "Never ask": "Nunca perguntar", + "Newest first": "Mais recentes primeiro", + "Next conversation": "Próxima conversa", + "Next period": "Próximo período", + "No availability information for {who}": "Sem informações de disponibilidade de {who}", + "No files inside — it carries only the formatted copy of the message.": "Nenhum arquivo dentro: ele traz apenas a cópia formatada da mensagem.", + "No verdict recorded": "Nenhum veredito registrado", + "Nobody here has free/busy on this server, so none of these rows can say whether anyone is free.": "Ninguém aqui tem livre/ocupado neste servidor, então nenhuma destas linhas pode dizer se alguém está livre.", + "Nothing (top level)": "Nada (nível superior)", + "Now, on your clock": "Agora, pelo seu relógio", + "Oldest first": "Mais antigas primeiro", + "Only the beginning is shown — download the file for the rest.": "Só o começo é exibido: baixe o arquivo para ver o resto.", + "Only when it has unread mail": "Somente quando houver mensagens não lidas", + "Open a link to {domain}?": "Abrir um link para {domain}?", + "Open it": "Abrir", + "Open links to these domains without asking": "Abrir links para estes domínios sem perguntar", + "Open, and stop asking about {domain}": "Abrir e parar de perguntar sobre {domain}", + "Opening…": "Abrindo…", + "Ordered by the server over the whole folder, not just the messages loaded so far. Ties always fall back to newest first, so the order never shuffles between two looks at the same folder.": "Ordenado pelo servidor sobre a pasta inteira, e não apenas sobre as mensagens carregadas até agora. Empates sempre recaem em mais recentes primeiro, de modo que a ordem nunca muda entre duas visitas à mesma pasta.", + "Placeholders": "Marcadores de posição", + "Previous conversation": "Conversa anterior", + "Previous period": "Período anterior", + "Privacy & safety": "Privacidade e segurança", + "Read receipts": "Confirmações de leitura", + "Reading, sending, and how dates and times are shown. What reaches a sender lives in Privacy & safety.": "Leitura, envio e como as datas e horas são exibidas. O que chega a um remetente fica em Privacidade e segurança.", + "Remote content": "Conteúdo remoto", + "Remove subscription": "Remover a assinatura", + "Remove {domain}": "Remover {domain}", + "Rendered": "Renderizado", + "Save changes": "Salvar alterações", + "Save filters": "Salvar filtros", + "Save your changes?": "Salvar suas alterações?", + "Saved": "Salvo", + "Select all {n} in {folder}": "Selecionar todas as {n} em {folder}", + "Send message": "Enviar mensagem", + "Send outside your organisation?": "Enviar para fora da sua organização?", + "Send to {count} people?": "Enviar para {count} pessoas?", + "Show birthdays from your contacts": "Exibir aniversários dos seus contatos", + "Show in the sidebar": "Exibir na barra lateral", + "Show keyboard shortcuts": "Exibir atalhos de teclado", + "Somebody else saved this file while it was open. Copy your changes, close it, and start again.": "Outra pessoa salvou este arquivo enquanto ele estava aberto. Copie as suas alterações, feche-o e comece de novo.", + "Sort by, in order": "Ordenar por, nesta ordem", + "Source": "Código-fonte", + "Spam filter": "Filtro de spam", + "Starred": "Favoritas", + "Starred first": "Favoritas primeiro", + "Stay here": "Ficar aqui", + "Stop trusting {address}": "Deixar de confiar em {address}", + "Subscribe to a calendar": "Assinar uma agenda", + "Subscribed calendar": "Agenda assinada", + "Subscribed calendars": "Agendas assinadas", + "Subscribed to {url}": "Assinado {url}", + "That file is no longer there.": "Esse arquivo não está mais lá.", + "That identity's address": "O endereço dessa identidade", + "The Inbox only": "Somente a Caixa de entrada", + "The message is held in this browser and has not been submitted yet, so taking it back costs nothing.": "A mensagem fica retida neste navegador e ainda não foi enviada, então recuperá-la não custa nada.", + "The name on the identity you are sending as": "O nome da identidade com a qual você está enviando", + "The subject already on the message": "O assunto que já está na mensagem", + "Their address": "O endereço dele", + "Their first name alone": "Apenas o primeiro nome dele", + "Then nothing": "Depois nada", + "There is no preview for this kind of file.": "Não há visualização para este tipo de arquivo.", + "There is nothing in it to export": "Não há nada nela para exportar", + "This file is not UTF-8 text, so editing it here would corrupt it — download it instead.": "Este arquivo não é texto UTF-8, então editá-lo aqui iria corrompê-lo: baixe-o em vez disso.", + "This file is too big to show here ({size}) — download it to read it.": "Este arquivo é grande demais para ser exibido aqui ({size}): baixe-o para lê-lo.", + "This goes to {recipients}{rest}.": "Isto vai para {recipients}{rest}.", + "This link does not go where it says": "Este link não leva para onde diz", + "This message packs its attachments into a winmail.dat, which most clients cannot open.": "Esta mensagem empacota os anexos em um winmail.dat, que a maioria dos clientes não consegue abrir.", + "Throw away your changes?": "Descartar suas alterações?", + "Today, in your date format": "Hoje, no seu formato de data", + "Unread first": "Não lidas primeiro", + "Unsaved changes": "Alterações não salvas", + "View as": "Exibir como", + "Warnings": "Avisos", + "Week view": "Visualização de semana", + "What is it called?": "Como se chama?", + "What reaches a sender, and what asks before it happens.": "O que chega a um remetente e o que pergunta antes que aconteça.", + "What you changed here will be lost.": "O que você alterou aqui será perdido.", + "Who the message is addressed to": "Para quem a mensagem está endereçada", + "Working out what is selected…": "Calculando a seleção…", + "You": "Você", + "Your Sieve script has changes that have not been saved.": "Seu script Sieve tem alterações que não foram salvas.", + "Your filter rules have changes that have not been saved.": "Suas regras de filtro têm alterações que não foram salvas.", + "Your own identity domains are always inside and do not need listing. A domain here also covers its subdomains.": "Os domínios das suas próprias identidades são sempre internos e não precisam ser listados. Um domínio aqui também abrange os seus subdomínios.", + "Your own:": "Os seus:", + "dark mode": "o modo escuro", + "file": "arquivo", + "light mode": "o modo claro", + "scored {score} against a threshold of {threshold}": "pontuou {score} para um limite de {threshold}", + "scored {score}, with no threshold stated": "pontuou {score}, sem limite informado", + "this view": "esta visualização", + "{count} conversations moved to {folder}": "{count} conversas movidas para {folder}", + "{count} folders": "{count} pastas", + "{name}’s birthday": "Aniversário de {name}", + "{name}’s birthday ({age})": "Aniversário de {name} ({age})", }, plurals: { "Updated {n} contacts, nothing new": { one: "{n} contato atualizado, nada novo", other: "{n} contatos atualizados, nada novo" }, From e5c85949018985b3fa90ba7d09476204a9286d21 Mon Sep 17 00:00:00 2001 From: John Coffey Date: Thu, 3 Sep 2026 13:55:19 -0700 Subject: [PATCH 2/2] Add the third-pass strings to Brazilian Portuguese The sweep in #259 found user-facing English in lib/ and store/ -- scheduled send, the read-receipt explanations, compose toasts and thrown errors -- plus two swipe labels that reach t() through a variable and so were invisible to a scan for t("literal") call sites. 17 strings and 3 plural forms. "{n} days" and "{n} hours" replace a suffix appended in the code, which is English grammar the catalogue could not reach. --- web/src/locales/pt-BR.ts | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/web/src/locales/pt-BR.ts b/web/src/locales/pt-BR.ts index 3366577..357cbee 100644 --- a/web/src/locales/pt-BR.ts +++ b/web/src/locales/pt-BR.ts @@ -1237,8 +1237,33 @@ export const catalog: Catalog = { "{count} folders": "{count} pastas", "{name}’s birthday": "Aniversário de {name}", "{name}’s birthday ({age})": "Aniversário de {name} ({age})", + // ── Third pass ────────────────────────────────────────────────────── + // Sentences that lib/ and store/ were building in English, and the two + // swipe labels that reach t() through a variable and so were invisible + // to a scan for t("literal"). See #259. + "A read receipt was already sent for this message.": "Já foi enviada uma confirmação de leitura para esta mensagem.", + "Add star": "Favoritar", + "Could not attach": "Não foi possível anexar", + "Delete forever?": "Excluir definitivamente?", + "Delete?": "Excluir?", + "No recipients": "Sem destinatários", + "No sending identity available": "Nenhuma identidade de envio disponível", + "Pick a date and time.": "Escolha uma data e uma hora.", + "Pick a time at least a minute from now.": "Escolha um horário pelo menos um minuto à frente.", + "Remove star": "Remover dos favoritos", + "Requested, to {address}. Never sent automatically.": "Solicitada, para {address}. Nunca é enviada automaticamente.", + "The sender did not request a read receipt.": "O remetente não solicitou confirmação de leitura.", + "This is bulk or list mail; read receipts for it only confirm the address is live.": "Isto é correio em massa ou de lista; uma confirmação de leitura apenas confirmaria que o endereço está ativo.", + "This message has not been received, so there is nothing to report.": "Esta mensagem não foi recebida, então não há nada a informar.", + "This message was sent automatically, so no read receipt is offered.": "Esta mensagem foi enviada automaticamente, então não há confirmação de leitura a oferecer.", + "This server will not hold a message longer than {span}.": "Este servidor não retém uma mensagem por mais de {span}.", + "Upload failed": "Falha no envio", }, plurals: { + // ── Third pass ───────────────────────────────────────────────────── + "Move {n} messages to Trash?": { one: "Mover {n} mensagem para a Lixeira?", other: "Mover {n} mensagens para a Lixeira?" }, + "{n} days": { one: "{n} dia", other: "{n} dias" }, + "{n} hours": { one: "{n} hora", other: "{n} horas" }, "Updated {n} contacts, nothing new": { one: "{n} contato atualizado, nada novo", other: "{n} contatos atualizados, nada novo" }, "{n} updated": { one: "{n} atualizado", other: "{n} atualizados" }, "Updated {n} contacts you already had": { one: "Contato que você já tinha atualizado", other: "{n} contatos que você já tinha atualizados" },