feat(i18n): translate five more systems
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
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
- Settings > Voice: video — preview, permission states, camera picker.
This closes the Voice & Video tab; input, output and video are all done.
- Create channel / category / space
- Group DMs — new DM, add friends, group settings, leave
- Join a space — discovery, invite code, and both federated auth phases
- Delete account, transfer ownership, and the top-level error screen
Three patterns worth noting. Strings that are chosen in a helper or held in
state (the camera's hard-blocked copy, its preview errors, the space
visibility options, the invite failure reasons) now travel as translation
keys and are resolved where they render, so switching language re-labels
what is already on screen. Sentences that wrap an emphasised literal — the
host name when joining, the space and member names when transferring — are
split around their placeholder instead of interpolated, leaving word order
to the translation. And `CreateChannel` mapped over `(t) =>` for the channel
type, which shadowed the translator; the loop variable is now `channelType`.
The error boundary in main.tsx is a class component, so it reads the locale
once per render via `translate` rather than the hook — correct here anyway,
since an error screen has no reason to re-render on a language switch.
Two GroupDmSettings tests located the name input by its aria-label, which
this change translates. They now use the `data-group-dm-name-input`
attribute the input already carries, matching how the rest of that file
locates elements — and no longer break when copy changes.
Dictionary is at 595 keys, en and pt-BR at exact parity.
Verified: web build (tsc + vite), 490 web tests.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -491,4 +491,177 @@ export const ptBR: Partial<Dictionary> = {
|
||||
'explore.request.send': 'Enviar pedido',
|
||||
'explore.request.sending': 'Enviando...',
|
||||
'explore.request.cancel': 'Cancelar',
|
||||
// Configurações — voz: vídeo
|
||||
'settings.video.title': 'Vídeo',
|
||||
'settings.video.checking': 'Verificando o acesso à câmera…',
|
||||
'settings.video.needPermission': 'Dê permissão de câmera para escolher uma.',
|
||||
'settings.video.promptNeeded': 'Precisa da permissão de câmera para escolher uma e ver a prévia.',
|
||||
'settings.video.enablePreview': 'Ligar a prévia da câmera',
|
||||
'settings.video.tryAgain': 'Tentar de novo',
|
||||
'settings.video.testCamera': 'Testar câmera',
|
||||
'settings.video.stopPreview': 'Parar prévia',
|
||||
'settings.video.auto': 'Automático (padrão do sistema)',
|
||||
'settings.video.currentlyUsing': 'Usando agora: {label}',
|
||||
'settings.video.detectedCamera': 'câmera detectada',
|
||||
'settings.video.noCameras': 'Nenhuma câmera detectada.',
|
||||
'settings.video.error.inUse': 'A câmera está sendo usada por outro programa.',
|
||||
'settings.video.error.unavailable': 'A câmera escolhida não está disponível.',
|
||||
'settings.video.error.notFound': 'Nenhuma câmera encontrada.',
|
||||
'settings.video.error.generic': 'Não foi possível iniciar a prévia da câmera.',
|
||||
'settings.video.blocked.mac': 'Libere a câmera em Ajustes do Sistema → Privacidade e Segurança → Câmera e reinicie o aplicativo.',
|
||||
'settings.video.blocked.windows': 'Libere a câmera em Configurações → Privacidade e Segurança → Câmera e reinicie o aplicativo.',
|
||||
'settings.video.blocked.browser': 'A permissão de câmera foi negada. Redefina no aviso do navegador e tente de novo.',
|
||||
|
||||
// Criar canal / categoria / servidor
|
||||
'create.cancel': 'Cancelar',
|
||||
'create.creating': 'Criando...',
|
||||
'create.channel.title': 'Criar canal',
|
||||
'create.channel.submit': 'Criar canal',
|
||||
'create.channel.type': 'Tipo de canal',
|
||||
'create.channel.type.text': 'Texto',
|
||||
'create.channel.type.voice': 'Voz',
|
||||
'create.channel.type.text.description': 'Enviar mensagens, imagens e arquivos',
|
||||
'create.channel.type.voice.description': 'Conversar por voz e vídeo',
|
||||
'create.channel.name': 'Nome do canal',
|
||||
'create.channel.name.placeholder': 'novo-canal',
|
||||
'create.channel.name.required': 'O nome do canal é obrigatório',
|
||||
'create.channel.topic': 'Tópico (opcional)',
|
||||
'create.channel.topic.placeholder': 'Sobre o que é este canal?',
|
||||
'create.channel.category': 'Categoria',
|
||||
'create.channel.category.none': 'Sem categoria',
|
||||
'create.channel.failed': 'Não foi possível criar o canal',
|
||||
'create.category.title': 'Criar categoria',
|
||||
'create.category.submit': 'Criar categoria',
|
||||
'create.category.name': 'Nome da categoria',
|
||||
'create.category.name.placeholder': 'nova-categoria',
|
||||
'create.category.name.required': 'O nome da categoria é obrigatório',
|
||||
'create.category.failed': 'Não foi possível criar a categoria',
|
||||
'create.noSpace': 'Nenhum servidor selecionado',
|
||||
'create.space.title': 'Criar um servidor',
|
||||
'create.space.submit': 'Criar',
|
||||
'create.space.upload': 'Enviar',
|
||||
'create.space.remove': 'Remover',
|
||||
'create.space.iconColor': 'Cor do ícone',
|
||||
'create.space.name': 'Nome do servidor',
|
||||
'create.space.name.placeholder': 'Meu servidor incrível',
|
||||
'create.space.name.required': 'O nome do servidor é obrigatório',
|
||||
'create.space.description': 'Descrição',
|
||||
'create.space.description.placeholder': 'Uma descrição curta do seu servidor...',
|
||||
'create.space.visibility': 'Visibilidade',
|
||||
'create.space.visibility.private': 'Privado',
|
||||
'create.space.visibility.private.description': 'Só entra quem tiver um link de convite',
|
||||
'create.space.visibility.request': 'Por pedido',
|
||||
'create.space.visibility.request.description': 'Aparece no Explorar — as pessoas pedem para entrar',
|
||||
'create.space.visibility.public': 'Público',
|
||||
'create.space.visibility.public.description': 'Aparece no Explorar — qualquer um entra na hora',
|
||||
'create.space.iconFailed': 'Não foi possível enviar o ícone',
|
||||
'create.space.failed': 'Não foi possível criar o servidor',
|
||||
'create.space.cropIcon': 'Recortar o ícone do servidor',
|
||||
|
||||
// DMs em grupo
|
||||
'groupDm.newDm.title': 'Nova mensagem direta',
|
||||
'groupDm.newDm.search': 'Buscar um usuário...',
|
||||
'groupDm.newDm.failed': 'Não foi possível criar a DM',
|
||||
'groupDm.add.title': 'Adicionar amigos à DM',
|
||||
'groupDm.add.search': 'Buscar amigos...',
|
||||
'groupDm.add.alreadyIn': 'Já está nesta DM',
|
||||
'groupDm.add.noMatch': 'Nenhum amigo encontrado',
|
||||
'groupDm.add.noFriends': 'Nenhum amigo ainda',
|
||||
'groupDm.add.select': 'Escolha os amigos',
|
||||
'groupDm.add.submit': 'Adicionar {count} amigo',
|
||||
'groupDm.add.submitPlural': 'Adicionar {count} amigos',
|
||||
'groupDm.add.adding': 'Adicionando...',
|
||||
'groupDm.add.failed': 'Não foi possível adicionar os membros',
|
||||
'groupDm.fallbackName': 'DM em grupo',
|
||||
'groupDm.icon.change': 'Trocar o ícone do grupo',
|
||||
'groupDm.icon.label': 'Ícone do grupo',
|
||||
'groupDm.icon.crop': 'Recortar o ícone do grupo',
|
||||
'groupDm.name': 'Nome do grupo',
|
||||
'groupDm.tab.general': 'Geral',
|
||||
'groupDm.tab.overview': 'Visão geral',
|
||||
'groupDm.tab.members': 'Membros',
|
||||
'groupDm.back': 'Voltar ao menu de configurações do grupo',
|
||||
'groupDm.save': 'Salvar',
|
||||
'groupDm.saving': 'Salvando...',
|
||||
'groupDm.cancel': 'Cancelar',
|
||||
'groupDm.close': 'Fechar',
|
||||
'groupDm.saveFailed': 'Não foi possível salvar as configurações',
|
||||
'groupDm.members.title': 'Membros',
|
||||
'groupDm.members.add': 'Adicionar membro',
|
||||
'groupDm.members.full': 'O grupo está cheio',
|
||||
'groupDm.leave': 'Sair do grupo',
|
||||
'groupDm.leaving': 'Saindo...',
|
||||
'groupDm.leave.description': 'Você para de receber as mensagens desta conversa. Os outros membros veem um aviso.',
|
||||
'groupDm.leave.confirm': 'Sair de "{name}"? Você para de receber as mensagens desta conversa.',
|
||||
'groupDm.leave.button': 'Sair',
|
||||
'groupDm.leave.failed': 'Não foi possível sair do grupo',
|
||||
|
||||
// Entrar num servidor
|
||||
'join.title': 'Entrar num servidor',
|
||||
'join.discover.lead': 'Descubra servidores para entrar, ou veja todos no Explorar.',
|
||||
'join.discover.error': 'Não deu para carregar os servidores agora. Você ainda pode entrar com um código de convite abaixo.',
|
||||
'join.discover.empty': 'Nenhum servidor para descobrir ainda — tente um código de convite abaixo, ou volte mais tarde.',
|
||||
'join.discover.browse': 'Ver todos no Explorar',
|
||||
'join.haveCode': 'Tem um código de convite?',
|
||||
'join.code.placeholder': 'ex.: abc123 ou https://instancia.com/join/abc123',
|
||||
'join.cancel': 'Cancelar',
|
||||
'join.back': 'Voltar',
|
||||
'join.submit': 'Entrar no servidor',
|
||||
'join.joining': 'Entrando...',
|
||||
'join.failed': 'Não foi possível entrar no servidor',
|
||||
'join.connect.lead': 'Conecte-se a {host} para entrar neste servidor.',
|
||||
'join.connect.password': 'Digite a sua senha para conectar',
|
||||
'join.connect.password.placeholder': 'A senha da sua conta',
|
||||
'join.connect.note': 'A sua senha é verificada aqui e depois usada para criar ou acessar a sua conta na outra instância.',
|
||||
'join.connect.submit': 'Conectar e entrar',
|
||||
'join.connect.connecting': 'Conectando...',
|
||||
'join.fallback.lead': 'Já existe uma conta em {host} com outra senha. Digite os dados que você usou naquela instância.',
|
||||
'join.fallback.username': 'Nome de usuário',
|
||||
'join.fallback.username.placeholder': 'Seu nome de usuário nessa instância',
|
||||
'join.fallback.password': 'Senha desta instância',
|
||||
'join.fallback.password.placeholder': 'Senha na instância remota',
|
||||
'join.fallback.submit': 'Entrar e participar',
|
||||
'join.fallback.loggingIn': 'Entrando...',
|
||||
|
||||
// Excluir conta / transferir propriedade / tela de erro
|
||||
'danger.delete.title': 'Excluir conta',
|
||||
'danger.delete.lead': 'Isto apaga a sua conta para sempre.',
|
||||
'danger.delete.item.spaces': 'Você sai de todos os servidores',
|
||||
'danger.delete.item.friends': 'Todas as amizades são desfeitas',
|
||||
'danger.delete.item.dms': 'Você sai de todas as DMs',
|
||||
'danger.delete.item.messages': 'Suas mensagens continuam, mas passam a aparecer como "Usuário excluído"',
|
||||
'danger.delete.owned': 'Você é dono de {count} servidor. Resolva antes de continuar:',
|
||||
'danger.delete.ownedPlural': 'Você é dono de {count} servidores. Resolva cada um antes de continuar:',
|
||||
'danger.delete.transfer': 'Transferir',
|
||||
'danger.delete.deleteSpace': 'Excluir servidor',
|
||||
'danger.delete.selectOwner': 'Escolha o novo dono...',
|
||||
'danger.delete.continue': 'Continuar',
|
||||
'danger.delete.processing': 'Processando...',
|
||||
'danger.delete.spacesFailed': 'Não foi possível resolver os servidores',
|
||||
'danger.delete.permanent': 'Isto é permanente e não dá para desfazer.',
|
||||
'danger.delete.password': 'Senha',
|
||||
'danger.delete.password.placeholder': 'Digite a sua senha',
|
||||
'danger.delete.back': 'Voltar',
|
||||
'danger.delete.confirm': 'Excluir minha conta',
|
||||
'danger.delete.deleting': 'Excluindo...',
|
||||
'danger.delete.usernameMismatch': 'O nome de usuário não confere',
|
||||
'danger.delete.failed': 'Não foi possível excluir a conta',
|
||||
'danger.delete.federating': 'Removendo a sua conta das instâncias conectadas...',
|
||||
'danger.delete.now': 'Excluir a conta agora',
|
||||
'danger.delete.done': 'Conta excluída',
|
||||
'danger.delete.redirecting': 'Voltando para a tela de login...',
|
||||
'danger.transfer.title': 'Transferir propriedade',
|
||||
'danger.transfer.lead': 'Escolha quem vai ser o novo dono de {space}',
|
||||
'danger.transfer.confirm': 'Transferir a propriedade de {space} para {member}?',
|
||||
'danger.transfer.note': 'Você passa a ser um membro comum.',
|
||||
'danger.transfer.search': 'Buscar membros...',
|
||||
'danger.transfer.loading': 'Carregando membros...',
|
||||
'danger.transfer.empty': 'Nenhum membro encontrado',
|
||||
'danger.transfer.cancel': 'Cancelar',
|
||||
'danger.transfer.submit': 'Transferir',
|
||||
'danger.transfer.transferring': 'Transferindo...',
|
||||
'danger.transfer.failed': 'Não foi possível transferir a propriedade',
|
||||
'error.title': 'Alguma coisa deu errado',
|
||||
'error.tryAgain': 'Tentar de novo',
|
||||
'error.reload': 'Recarregar a página',
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user