diff --git a/docs/roadmap-resenha.md b/docs/roadmap-resenha.md index 1e3e8b08..0db21249 100644 --- a/docs/roadmap-resenha.md +++ b/docs/roadmap-resenha.md @@ -100,11 +100,18 @@ fixas por `t('chave')`. O que faltar cai para o inglês sozinho. - Configurações → Atalhos (incluindo os nomes das ações e o aviso de conflito) - Configurações → Conta (perfil, senha, conta desligada da origem, zona de perigo) - Explorar servidores (cartões, entrar, pedir para entrar) +- Configurações → Voz: vídeo (prévia, permissões, escolha de câmera) — fecha a aba +- Criar canal, categoria e servidor +- DMs em grupo (nova DM, adicionar amigos, configurações do grupo, sair) +- Entrar num servidor (descoberta, código de convite, conectar e login federado) +- Excluir conta, transferir propriedade e a tela de erro geral ### Fila sugerida de tradução -Configurações → Vídeo · Configurações → Instância · configurações do servidor e -cargos · telas de erro · DMs em grupo (`GroupDmSettings`, `AddDmMemberModal`) +Configurações → Instância (painéis: geral, registro, federação, transmissão, +armazenamento, usuários) · configurações do servidor e cargos +(`SpaceSettings` e `spaceSettingsPanels/`) · telas móveis (`Mobile*.tsx`) · +`CategorySettingsModal` · `ConnectedInstances` ## Correções pendentes diff --git a/packages/web/src/components/modals/AddDmMemberModal.tsx b/packages/web/src/components/modals/AddDmMemberModal.tsx index 85aaf25d..c32e3093 100644 --- a/packages/web/src/components/modals/AddDmMemberModal.tsx +++ b/packages/web/src/components/modals/AddDmMemberModal.tsx @@ -10,6 +10,7 @@ import { api } from '../../api/client'; import { isSelf, parseFederatedUsername } from '../../utils/identity'; import { useCanonicalUserView } from '../../utils/userViewLookup'; import type { User } from '@backspace/shared'; +import { useT } from '../../i18n'; function AddDmFriendRow({ friend, @@ -26,6 +27,7 @@ function AddDmFriendRow({ isAdding: boolean; onToggle: (id: string) => void; }) { + const t = useT(); const canonical = useCanonicalUserView(friend as unknown as User); const { baseName } = parseFederatedUsername(canonical.username); const friendDisplayName = canonical.displayName ?? baseName; @@ -54,7 +56,7 @@ function AddDmFriendRow({ {friendDisplayName}
- {isInDm ? 'Already in this DM' : `@${canonical.username}`} + {isInDm ? t('groupDm.add.alreadyIn') : `@${canonical.username}`}
{!isInDm && ( @@ -77,6 +79,7 @@ function AddDmFriendRow({ } export function AddDmMemberModal() { + const t = useT(); const [query, setQuery] = useState(''); const [selected, setSelected] = useState>(new Set()); const [error, setError] = useState(''); @@ -190,18 +193,20 @@ export function AddDmMemberModal() { closeModal(); } } catch (err) { - setError((err as Error).message || 'Failed to add members'); + setError((err as Error).message || t('groupDm.add.failed')); } finally { setIsAdding(false); } }; const buttonText = selectedFriends.length === 0 - ? 'Select Friends' - : `Add ${selectedFriends.length} Friend${selectedFriends.length > 1 ? 's' : ''}`; + ? t('groupDm.add.select') + : t(selectedFriends.length > 1 ? 'groupDm.add.submitPlural' : 'groupDm.add.submit', { + count: selectedFriends.length, + }); return ( - +
{/* Header with member count */}
@@ -239,7 +244,7 @@ export function AddDmMemberModal() { type="text" value={query} onChange={(e) => setQuery(e.target.value)} - placeholder="Search friends..." + placeholder={t('groupDm.add.search')} className="input-search w-full py-2 text-[14px]" disabled={remainingSlots <= 0} /> @@ -256,7 +261,7 @@ export function AddDmMemberModal() {
{filteredFriends.length === 0 && (
- {query.trim() ? 'No friends match your search' : 'No friends yet'} + {query.trim() ? t('groupDm.add.noMatch') : t('groupDm.add.noFriends')}
)} @@ -284,7 +289,7 @@ export function AddDmMemberModal() { disabled={selectedFriends.length === 0 || isAdding} className="w-full py-2 rounded-md text-[13px] font-semibold transition-colors bg-accent-mint text-surface-base hover:bg-accent-mint/90 disabled:opacity-50 disabled:cursor-not-allowed" > - {isAdding ? 'Adding...' : buttonText} + {isAdding ? t('groupDm.add.adding') : buttonText}
diff --git a/packages/web/src/components/modals/CreateCategory.tsx b/packages/web/src/components/modals/CreateCategory.tsx index b771a8b8..85d9ee6e 100644 --- a/packages/web/src/components/modals/CreateCategory.tsx +++ b/packages/web/src/components/modals/CreateCategory.tsx @@ -2,8 +2,10 @@ import React, { useState } from 'react'; import { Modal } from '../ui/Modal'; import { useUIStore } from '../../stores/uiStore'; import { useSpaceStore } from '../../stores/spaceStore'; +import { useT } from '../../i18n'; export function CreateCategoryModal() { + const t = useT(); const [name, setName] = useState(''); const [error, setError] = useState(''); const [isLoading, setIsLoading] = useState(false); @@ -19,12 +21,12 @@ export function CreateCategoryModal() { setError(''); if (!name.trim()) { - setError('Category name is required'); + setError(t('create.category.name.required')); return; } if (!currentSpaceId) { - setError('No space selected'); + setError(t('create.noSpace')); return; } @@ -34,14 +36,14 @@ export function CreateCategoryModal() { closeModal(); setName(''); } catch (err) { - setError(err instanceof Error ? err.message : 'Failed to create category'); + setError(err instanceof Error ? err.message : t('create.category.failed')); } finally { setIsLoading(false); } }; return ( - +
{error && (
@@ -51,14 +53,14 @@ export function CreateCategoryModal() {
setName(e.target.value)} className="input-standard w-full" - placeholder="new-category" + placeholder={t('create.category.name.placeholder')} autoFocus />
@@ -71,14 +73,14 @@ export function CreateCategoryModal() { onClick={closeModal} className="px-3 py-1 text-sm text-txt-tertiary hover:text-txt-secondary transition-colors" > - Cancel + {t('create.cancel')}
diff --git a/packages/web/src/components/modals/CreateChannel.tsx b/packages/web/src/components/modals/CreateChannel.tsx index e8c11653..aae35766 100644 --- a/packages/web/src/components/modals/CreateChannel.tsx +++ b/packages/web/src/components/modals/CreateChannel.tsx @@ -2,8 +2,10 @@ import React, { useState, useEffect } from 'react'; import { Modal } from '../ui/Modal'; import { useUIStore } from '../../stores/uiStore'; import { useSpaceStore } from '../../stores/spaceStore'; +import { useT } from '../../i18n'; export function CreateChannelModal() { + const t = useT(); const [name, setName] = useState(''); const [type, setType] = useState<'text' | 'voice'>('text'); const [topic, setTopic] = useState(''); @@ -31,12 +33,12 @@ export function CreateChannelModal() { setError(''); if (!name.trim()) { - setError('Channel name is required'); + setError(t('create.channel.name.required')); return; } if (!currentSpaceId) { - setError('No space selected'); + setError(t('create.noSpace')); return; } @@ -49,14 +51,14 @@ export function CreateChannelModal() { setType('text'); setCategoryId(''); } catch (err) { - setError(err instanceof Error ? err.message : 'Failed to create channel'); + setError(err instanceof Error ? err.message : t('create.channel.failed')); } finally { setIsLoading(false); } }; return ( - + {error && (
@@ -66,14 +68,14 @@ export function CreateChannelModal() {
- {(['text', 'voice'] as const).map((t) => ( + {(['text', 'voice'] as const).map((channelType) => ( @@ -108,14 +110,14 @@ export function CreateChannelModal() {
setName(e.target.value)} className="input-standard w-full" - placeholder="new-channel" + placeholder={t('create.channel.name.placeholder')} autoFocus />
@@ -123,14 +125,14 @@ export function CreateChannelModal() { {type === 'text' && (
setTopic(e.target.value)} className="input-standard w-full" - placeholder="What's this channel about?" + placeholder={t('create.channel.topic.placeholder')} />
)} @@ -138,14 +140,14 @@ export function CreateChannelModal() { {categories.length > 0 && (
setName(e.target.value)} className="input-standard w-full" - placeholder="My Awesome Space" + placeholder={t('create.space.name.placeholder')} autoFocus />
@@ -226,7 +229,7 @@ export function CreateSpaceModal() { {/* Visibility */}
- Visibility + {t('create.space.visibility')}
{visibilityOptions.map((opt) => ( @@ -247,8 +250,8 @@ export function CreateSpaceModal() { className="mt-0.5 accent-accent-primary" />
-
{opt.label}
-
{opt.desc}
+
{t(opt.labelKey)}
+
{t(opt.descKey)}
))} @@ -258,12 +261,12 @@ export function CreateSpaceModal() { {/* Description */}
- Description + {t('create.space.description')}