@@ -523,7 +526,7 @@ export function VideoSection() {
@@ -532,7 +535,7 @@ export function VideoSection() {
Click to test camera
)}
- {previewActive && !previewError && (
+ {previewActive && !previewErrorKey && (
- Stop preview
+ {t('settings.video.stopPreview')}
)}
- {previewError && (
+ {previewErrorKey && (
-
{previewError}
+
{t(previewErrorKey)}
- Try again
+ {t('settings.video.tryAgain')}
)}
@@ -579,7 +582,7 @@ export function VideoSection() {
{listOpen && (
handleSelect(null)}
/>
@@ -599,13 +602,14 @@ export function VideoSection() {
(previewActive || isCameraOn) &&
(() => {
const m = devices.find((d) => d.deviceId === activeDeviceId);
+ const fallback = t('settings.video.detectedCamera');
const label = m
- ? displayLabels.get(activeDeviceId) ?? (m.label || 'detected camera')
- : 'detected camera';
- return Currently using: {label}
;
+ ? displayLabels.get(activeDeviceId) ?? (m.label || fallback)
+ : fallback;
+ return {t('settings.video.currentlyUsing', { label })}
;
})()}
{devices.length === 0 && (
- No cameras detected.
+ {t('settings.video.noCameras')}
)}
diff --git a/packages/web/src/i18n/locales/en.ts b/packages/web/src/i18n/locales/en.ts
index 80285084..da82c3b0 100644
--- a/packages/web/src/i18n/locales/en.ts
+++ b/packages/web/src/i18n/locales/en.ts
@@ -492,6 +492,179 @@ export const en = {
'explore.request.send': 'Send Request',
'explore.request.sending': 'Sending...',
'explore.request.cancel': 'Cancel',
+ // Settings — voice: video
+ 'settings.video.title': 'Video',
+ 'settings.video.checking': 'Checking camera access…',
+ 'settings.video.needPermission': 'Grant camera permission to choose a camera.',
+ 'settings.video.promptNeeded': 'Camera permission needed to choose a camera and preview.',
+ 'settings.video.enablePreview': 'Enable camera preview',
+ 'settings.video.tryAgain': 'Try again',
+ 'settings.video.testCamera': 'Test camera',
+ 'settings.video.stopPreview': 'Stop preview',
+ 'settings.video.auto': 'Auto (system default)',
+ 'settings.video.currentlyUsing': 'Currently using: {label}',
+ 'settings.video.detectedCamera': 'detected camera',
+ 'settings.video.noCameras': 'No cameras detected.',
+ 'settings.video.error.inUse': 'Camera is in use by another application.',
+ 'settings.video.error.unavailable': 'Selected camera is unavailable.',
+ 'settings.video.error.notFound': 'No camera detected.',
+ 'settings.video.error.generic': 'Could not start camera preview.',
+ 'settings.video.blocked.mac': 'Grant camera access in System Settings → Privacy & Security → Camera, then restart the app.',
+ 'settings.video.blocked.windows': 'Grant camera access in Settings → Privacy & Security → Camera, then restart the app.',
+ 'settings.video.blocked.browser': 'Camera permission was denied. Reset it in your browser/Chromium prompt and try again.',
+
+ // Create channel / category / space
+ 'create.cancel': 'Cancel',
+ 'create.creating': 'Creating...',
+ 'create.channel.title': 'Create Channel',
+ 'create.channel.submit': 'Create Channel',
+ 'create.channel.type': 'Channel Type',
+ 'create.channel.type.text': 'Text',
+ 'create.channel.type.voice': 'Voice',
+ 'create.channel.type.text.description': 'Send messages, images, and files',
+ 'create.channel.type.voice.description': 'Hang out with voice and video',
+ 'create.channel.name': 'Channel Name',
+ 'create.channel.name.placeholder': 'new-channel',
+ 'create.channel.name.required': 'Channel name is required',
+ 'create.channel.topic': 'Topic (optional)',
+ 'create.channel.topic.placeholder': "What's this channel about?",
+ 'create.channel.category': 'Category',
+ 'create.channel.category.none': 'No Category',
+ 'create.channel.failed': 'Failed to create channel',
+ 'create.category.title': 'Create Category',
+ 'create.category.submit': 'Create Category',
+ 'create.category.name': 'Category Name',
+ 'create.category.name.placeholder': 'new-category',
+ 'create.category.name.required': 'Category name is required',
+ 'create.category.failed': 'Failed to create category',
+ 'create.noSpace': 'No space selected',
+ 'create.space.title': 'Create a Space',
+ 'create.space.submit': 'Create',
+ 'create.space.upload': 'Upload',
+ 'create.space.remove': 'Remove',
+ 'create.space.iconColor': 'Icon Color',
+ 'create.space.name': 'Space Name',
+ 'create.space.name.placeholder': 'My Awesome Space',
+ 'create.space.name.required': 'Space name is required',
+ 'create.space.description': 'Description',
+ 'create.space.description.placeholder': 'A short description for your space...',
+ 'create.space.visibility': 'Visibility',
+ 'create.space.visibility.private': 'Private',
+ 'create.space.visibility.private.description': 'Only people with an invite link can join',
+ 'create.space.visibility.request': 'Request to Join',
+ 'create.space.visibility.request.description': 'Visible in Explore — people can request to join',
+ 'create.space.visibility.public': 'Public',
+ 'create.space.visibility.public.description': 'Visible in Explore — anyone can join instantly',
+ 'create.space.iconFailed': 'Failed to upload icon',
+ 'create.space.failed': 'Failed to create space',
+ 'create.space.cropIcon': 'Crop Space Icon',
+
+ // Group DMs
+ 'groupDm.newDm.title': 'New Direct Message',
+ 'groupDm.newDm.search': 'Search for a user...',
+ 'groupDm.newDm.failed': 'Failed to create DM',
+ 'groupDm.add.title': 'Add Friends to DM',
+ 'groupDm.add.search': 'Search friends...',
+ 'groupDm.add.alreadyIn': 'Already in this DM',
+ 'groupDm.add.noMatch': 'No friends match your search',
+ 'groupDm.add.noFriends': 'No friends yet',
+ 'groupDm.add.select': 'Select Friends',
+ 'groupDm.add.submit': 'Add {count} Friend',
+ 'groupDm.add.submitPlural': 'Add {count} Friends',
+ 'groupDm.add.adding': 'Adding...',
+ 'groupDm.add.failed': 'Failed to add members',
+ 'groupDm.fallbackName': 'Group DM',
+ 'groupDm.icon.change': 'Change group icon',
+ 'groupDm.icon.label': 'Group icon',
+ 'groupDm.icon.crop': 'Crop Group Icon',
+ 'groupDm.name': 'Group Name',
+ 'groupDm.tab.general': 'General',
+ 'groupDm.tab.overview': 'Overview',
+ 'groupDm.tab.members': 'Members',
+ 'groupDm.back': 'Back to group DM settings menu',
+ 'groupDm.save': 'Save',
+ 'groupDm.saving': 'Saving...',
+ 'groupDm.cancel': 'Cancel',
+ 'groupDm.close': 'Close',
+ 'groupDm.saveFailed': 'Failed to save settings',
+ 'groupDm.members.title': 'Members',
+ 'groupDm.members.add': 'Add Member',
+ 'groupDm.members.full': 'Group is full',
+ 'groupDm.leave': 'Leave Group',
+ 'groupDm.leaving': 'Leaving...',
+ 'groupDm.leave.description': 'You will stop receiving messages from this conversation. Other members will see a system message.',
+ 'groupDm.leave.confirm': 'Leave "{name}"? You will stop receiving messages from this conversation.',
+ 'groupDm.leave.button': 'Leave',
+ 'groupDm.leave.failed': 'Failed to leave group',
+
+ // Join a space
+ 'join.title': 'Join a Space',
+ 'join.discover.lead': 'Discover spaces to join, or browse them all in Explore.',
+ 'join.discover.error': 'Couldn’t load spaces to discover right now. You can still join with an invite code below.',
+ 'join.discover.empty': 'No spaces to discover yet — try an invite code below, or check back later.',
+ 'join.discover.browse': 'Browse all in Explore',
+ 'join.haveCode': 'Have an invite code?',
+ 'join.code.placeholder': 'e.g. abc123 or https://instance.com/join/abc123',
+ 'join.cancel': 'Cancel',
+ 'join.back': 'Back',
+ 'join.submit': 'Join Space',
+ 'join.joining': 'Joining...',
+ 'join.failed': 'Failed to join space',
+ 'join.connect.lead': 'Connect to {host} to join this space.',
+ 'join.connect.password': 'Enter your password to connect',
+ 'join.connect.password.placeholder': 'Your account password',
+ 'join.connect.note': 'Your password is verified locally, then used to create or access your account on the remote instance.',
+ 'join.connect.submit': 'Connect & Join',
+ 'join.connect.connecting': 'Connecting...',
+ 'join.fallback.lead': 'An account already exists on {host} with a different password. Enter the credentials you used on that instance.',
+ 'join.fallback.username': 'Username',
+ 'join.fallback.username.placeholder': 'Your username on this instance',
+ 'join.fallback.password': 'Password for this instance',
+ 'join.fallback.password.placeholder': 'Password on the remote instance',
+ 'join.fallback.submit': 'Login & Join',
+ 'join.fallback.loggingIn': 'Logging in...',
+
+ // Delete account / transfer ownership / error screen
+ 'danger.delete.title': 'Delete Account',
+ 'danger.delete.lead': 'This will permanently delete your account.',
+ 'danger.delete.item.spaces': 'All space memberships will be removed',
+ 'danger.delete.item.friends': 'All friend connections will be removed',
+ 'danger.delete.item.dms': 'All DM memberships will be removed',
+ 'danger.delete.item.messages': 'Your messages will remain but be attributed to "Deleted User"',
+ 'danger.delete.owned': 'You own {count} space. Handle it before continuing:',
+ 'danger.delete.ownedPlural': 'You own {count} spaces. Handle each before continuing:',
+ 'danger.delete.transfer': 'Transfer',
+ 'danger.delete.deleteSpace': 'Delete Space',
+ 'danger.delete.selectOwner': 'Select new owner...',
+ 'danger.delete.continue': 'Continue',
+ 'danger.delete.processing': 'Processing...',
+ 'danger.delete.spacesFailed': 'Failed to process spaces',
+ 'danger.delete.permanent': 'This action is permanent and cannot be undone.',
+ 'danger.delete.password': 'Password',
+ 'danger.delete.password.placeholder': 'Enter your password',
+ 'danger.delete.back': 'Back',
+ 'danger.delete.confirm': 'Delete My Account',
+ 'danger.delete.deleting': 'Deleting...',
+ 'danger.delete.usernameMismatch': 'Username does not match',
+ 'danger.delete.failed': 'Failed to delete account',
+ 'danger.delete.federating': 'Removing your account from connected instances...',
+ 'danger.delete.now': 'Delete Account Now',
+ 'danger.delete.done': 'Account deleted',
+ 'danger.delete.redirecting': 'Redirecting to login...',
+ 'danger.transfer.title': 'Transfer Ownership',
+ 'danger.transfer.lead': 'Choose a member to become the new owner of {space}',
+ 'danger.transfer.confirm': 'Transfer ownership of {space} to {member}?',
+ 'danger.transfer.note': 'You will become a regular member.',
+ 'danger.transfer.search': 'Search members...',
+ 'danger.transfer.loading': 'Loading members...',
+ 'danger.transfer.empty': 'No members found',
+ 'danger.transfer.cancel': 'Cancel',
+ 'danger.transfer.submit': 'Transfer',
+ 'danger.transfer.transferring': 'Transferring...',
+ 'danger.transfer.failed': 'Failed to transfer ownership',
+ 'error.title': 'Something went wrong',
+ 'error.tryAgain': 'Try Again',
+ 'error.reload': 'Reload Page',
} as const;
export type TranslationKey = keyof typeof en;
diff --git a/packages/web/src/i18n/locales/pt-BR.ts b/packages/web/src/i18n/locales/pt-BR.ts
index a030ad90..697a9a94 100644
--- a/packages/web/src/i18n/locales/pt-BR.ts
+++ b/packages/web/src/i18n/locales/pt-BR.ts
@@ -491,4 +491,177 @@ export const ptBR: Partial
= {
'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',
};
diff --git a/packages/web/src/main.tsx b/packages/web/src/main.tsx
index 8647ad09..b1ddecf4 100644
--- a/packages/web/src/main.tsx
+++ b/packages/web/src/main.tsx
@@ -4,6 +4,7 @@ import { BrowserRouter } from 'react-router-dom';
import { App } from './App';
import { startPendingMessageOrchestrator } from './stores/pendingMessageRehydrate';
import './styles/globals.css';
+import { translate, useLocaleStore } from './i18n';
class ErrorBoundary extends React.Component<
{ children: React.ReactNode },
@@ -33,6 +34,11 @@ class ErrorBoundary extends React.Component<
render() {
if (this.state.hasError) {
+ // Class component: no hooks. Reading the locale once per render is also
+ // the right call here — an error screen has no reason to re-render on a
+ // language switch, and `translate` falls back to English then to the key.
+ const tr = (key: Parameters[1]) =>
+ translate(useLocaleStore.getState().locale, key);
return (
-
Something went wrong
+
{tr('error.title')}
{this.state.error?.message}
- Try Again
+ {tr('error.tryAgain')}
window.location.reload()}
@@ -77,7 +83,7 @@ class ErrorBoundary extends React.Component<
fontFamily: "'DM Sans', sans-serif",
}}
>
- Reload Page
+ {tr('error.reload')}
{this.state.error?.stack && (