feat(i18n): translate five more systems, and check for updates more often
CI / Build & test (Node 20) (push) Canceled after 0s
CI / Build & test (Node 24) (push) Canceled after 0s
CI / Build & test (push) Canceled after 0s
CodeQL / Analyze (javascript-typescript) (push) Canceled after 0s
Security / Secret scan (gitleaks) (push) Canceled after 0s
Security / Dependency scan (OSV-Scanner) (push) Canceled after 0s
Security / IaC/config scan (Trivy) (push) Canceled after 0s
Security / License compliance scan (Trivy) (push) Canceled after 0s
OpenSSF Scorecard / Scorecard analysis (push) Canceled after 0s

Update cadence: the desktop app checked once ten seconds after launch and
then every four hours. A release published while the app sits open went
unnoticed until the next poll — which is exactly what happened with 1.4.0.
It now polls every 30 minutes and also checks when the window regains focus,
throttled to one check per five minutes so alt-tabbing does not hammer the
feed. `lastCheckAt` is stamped from the `checking-for-update` event rather
than by the callers, so a manual check from the settings panel counts too.
The focus listener is on `app`, not on mainWindow, because the window is
destroyed and recreated when closed to tray.

Translations, five whole systems:
  - Settings > Desktop (autostart, updates, instance)
  - Settings > Voice: output device, which completes that tab
  - Settings > Keybinds, including the action names and conflict prompt
  - Settings > Account, the largest — profile, password, detached-instance
    notice, danger zone
  - Explore spaces

Two sentences wrap a literal that must not be translated (the `input` group
name, the `user@instance` example). Those are split around a placeholder
rather than interpolated as text, so a translation keeps control of the word
order on both sides of it. Keybind action labels gained a `labelKey`
alongside the English `label`, which stays for non-React callers.

Verified: web build (tsc + vite), desktop typecheck, 490 web tests.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-02 13:38:28 -03:00
co-authored by Claude Opus 5
parent a54d998ba9
commit 115c81cba3
10 changed files with 448 additions and 132 deletions
+129
View File
@@ -362,4 +362,133 @@ export const ptBR: Partial<Dictionary> = {
'invite.fail.invalid_target': 'Destino inválido',
'invite.fail.network': 'Não deu para falar com a sua instância',
'invite.fail.generic': 'Não deu para enviar (erro no servidor)',
// Configurações — área de trabalho
'settings.desktop.title': 'Área de trabalho',
'settings.desktop.startAtBoot': 'Abrir junto com o sistema',
'settings.desktop.startAtBoot.description': 'Inicia o Backspace sozinho quando você faz login',
'settings.desktop.startMinimized': 'Abrir minimizado',
'settings.desktop.startMinimized.description': 'Começa escondido na bandeja em vez de abrir a janela',
'settings.desktop.version': 'Versão {version}',
'settings.desktop.appName': 'Backspace Desktop',
'settings.desktop.update.description': 'Procurar versões novas do aplicativo',
'settings.desktop.update.check': 'Procurar atualizações',
'settings.desktop.update.checking': 'Procurando...',
'settings.desktop.instance.current': 'Instância conectada agora',
'settings.desktop.instance.change': 'Trocar de instância',
// Configurações — voz: saída
'settings.output.title': 'Dispositivo de saída',
'settings.output.checking': 'Verificando o acesso ao áudio…',
'settings.output.systemDefault': 'Padrão do sistema',
'settings.output.unsupported': 'Este navegador não deixa escolher o dispositivo de saída. O áudio toca no padrão do sistema.',
'settings.output.needPermission': 'Dê permissão de microfone para listar os dispositivos de saída (os navegadores escondem os nomes de saída atrás do acesso ao microfone).',
'settings.output.enableAccess': 'Liberar acesso ao áudio',
'settings.output.volume': 'Volume de saída',
'settings.output.testSound': 'Tocar som de teste',
// Configurações — atalhos
'settings.keybinds.title': 'Atalhos',
'settings.keybinds.section.voice': 'Atalhos de voz',
'settings.keybinds.pressCombo': 'Aperte a combinação...',
'settings.keybinds.notBound': 'Sem atalho',
'settings.keybinds.edit': 'Editar',
'settings.keybinds.record': 'Gravar',
'settings.keybinds.delete': 'Excluir',
'settings.keybinds.escToCancel': 'ESC para cancelar',
'settings.keybinds.accessibility.title': 'Precisa da permissão de Acessibilidade',
'settings.keybinds.accessibility.description': 'O Backspace precisa da permissão de Acessibilidade para os atalhos funcionarem fora do aplicativo.',
'settings.keybinds.accessibility.grant': 'Conceder permissão',
'settings.keybinds.hookError.title': 'Atalhos globais indisponíveis',
'settings.keybinds.hookError.description': 'Não foi possível iniciar o leitor de entrada. No Linux, talvez o seu usuário precise estar no grupo {group}.',
'settings.keybinds.webNote': 'Os atalhos funcionam enquanto esta aba estiver em foco. Para atalhos que funcionam em outros programas, use o aplicativo.',
'settings.keybinds.conflict.suffix': '. Substituir?',
'settings.keybinds.conflict.boundTo': 'já está ligado a',
'settings.keybinds.conflict.overwrite': 'Substituir',
'settings.keybinds.conflict.cancel': 'Cancelar',
'settings.keybinds.action.toggleMute': 'Ligar/desligar microfone',
'settings.keybinds.action.toggleDeafen': 'Ligar/desligar som',
'settings.keybinds.action.pushToTalk': 'Apertar para falar',
'settings.keybinds.action.toggleCamera': 'Ligar/desligar câmera',
'settings.keybinds.action.toggleScreenShare': 'Ligar/desligar transmissão',
'settings.keybinds.action.disconnect': 'Desconectar',
// Configurações — conta
'settings.account.title': 'Minha conta',
'settings.account.detached.lead': 'Esta conta está desligada da instância de origem.',
'settings.account.detached.body': 'A {instance} foi reiniciada ou não existe mais, então esta conta agora funciona localmente nesta instância — o perfil e a senha são gerenciados aqui.',
'settings.account.detached.relink': 'Como {username} em {instance}, dá para religar esta conta — o perfil e a presença voltam a sincronizar de lá.',
'settings.account.detached.reattaching': 'Religando…',
'settings.account.detached.confirm': 'Confirmar religação como {username}',
'settings.account.detached.reattach': 'Religar a {instance}',
'settings.account.detached.failed': 'Não foi possível religar',
'settings.account.section.profile': 'Personalização do perfil',
'settings.account.section.account': 'Conta',
'settings.account.section.password': 'Senha',
'settings.account.section.danger': 'Zona de perigo',
'settings.account.avatar': 'Foto de perfil',
'settings.account.avatar.change': 'Trocar foto',
'settings.account.banner': 'Banner',
'settings.account.banner.change': 'Trocar banner',
'settings.account.banner.chooseGif': 'Escolher GIF',
'settings.account.remove': 'Remover',
'settings.account.clear': 'Limpar',
'settings.account.avatarColor': 'Cor da foto',
'settings.account.bannerColor': 'Cor do banner',
'settings.account.aboutMe': 'Sobre mim',
'settings.account.aboutMe.placeholder': 'Conte alguma coisa sobre você...',
'settings.account.status': 'Status',
'settings.account.status.online': 'Online',
'settings.account.status.idle': 'Ausente',
'settings.account.status.dnd': 'Não perturbe',
'settings.account.displayName': 'Nome de exibição',
'settings.account.customStatus': 'Status personalizado',
'settings.account.customStatus.placeholder': 'O que você está fazendo?',
'settings.account.password.current': 'Senha atual',
'settings.account.password.current.placeholder': 'Digite a senha atual',
'settings.account.password.new': 'Senha nova',
'settings.account.password.new.placeholder': 'Mínimo de 6 caracteres',
'settings.account.password.confirm': 'Confirmar a senha nova',
'settings.account.password.confirm.placeholder': 'Confirme a senha nova',
'settings.account.password.change': 'Trocar senha',
'settings.account.password.changing': 'Trocando...',
'settings.account.password.changed': 'Senha trocada',
'settings.account.password.tooShort': 'A senha nova precisa ter pelo menos 8 caracteres',
'settings.account.password.mismatch': 'As senhas não batem',
'settings.account.password.failed': 'Não foi possível trocar a senha',
'settings.account.sync.synced': 'Sincronizado',
'settings.account.sync.failed': 'Falhou — sincroniza quando reconectar',
'settings.account.delete.warning': 'Depois de excluir a conta não tem volta. Suas mensagens continuam lá, mas passam a aparecer como "Usuário excluído".',
'settings.account.delete': 'Excluir conta',
'settings.account.reset': 'Desfazer',
'settings.account.save': 'Salvar',
'settings.account.saving': 'Salvando...',
'settings.account.saved': 'Perfil atualizado',
'settings.account.saveFailed': 'Não foi possível atualizar o perfil',
'settings.account.avatar.uploadFailed': 'Não foi possível enviar a foto',
'settings.account.banner.uploadFailed': 'Não foi possível enviar o banner',
'settings.account.crop.avatar': 'Recortar a foto',
'settings.account.crop.banner': 'Recortar o banner',
// Explorar servidores
'explore.title': 'Explorar',
'explore.searchPlaceholder': 'Buscar servidores...',
'explore.disabled': 'A descoberta de servidores está desativada pelo administrador da instância.',
'explore.empty.search': 'Nenhum servidor encontrado.',
'explore.empty.none': 'Nenhum servidor para descobrir ainda.',
'explore.allJoined': 'Você já está em todos os servidores visíveis',
'explore.joined': 'Você participa',
'explore.badge.public': 'Público',
'explore.badge.request': 'Por pedido',
'explore.noDescription': 'Sem descrição',
'explore.member': 'membro',
'explore.members': 'membros',
'explore.view': 'Ver servidor',
'explore.join': 'Entrar no servidor',
'explore.joining': 'Entrando...',
'explore.requestPending': 'Pedido pendente',
'explore.requestToJoin': 'Pedir para entrar',
'explore.request.placeholder': 'Por que você quer entrar? (opcional)',
'explore.request.send': 'Enviar pedido',
'explore.request.sending': 'Enviando...',
'explore.request.cancel': 'Cancelar',
};