From 61c68761df816b3af0d8f62cbd1755ae735086fb Mon Sep 17 00:00:00 2001 From: devsyncwrld Date: Tue, 1 Sep 2026 17:03:48 -0300 Subject: [PATCH] i18n: translate the channel and member sidebars Clears the two remaining rounds owed under the fork's per-update translation rule. Covers channels, direct messages, the context menus, the inline voice settings and the member list's loading and grouping labels. The member grouping needed care: one function returns either a fixed label or a role's own name, and role names are user data that must never be translated. It now marks only the fixed cases and the component resolves them at render, leaving role names untouched. The function is not a component, so it cannot call the hook itself. --- docs/roadmap-resenha.md | 2 + .../src/components/layout/ChannelSidebar.tsx | 45 ++++++++++--------- .../src/components/layout/MemberSidebar.tsx | 17 +++++-- packages/web/src/i18n/locales/en.ts | 22 +++++++++ packages/web/src/i18n/locales/pt-BR.ts | 22 +++++++++ 5 files changed, 83 insertions(+), 25 deletions(-) diff --git a/docs/roadmap-resenha.md b/docs/roadmap-resenha.md index 2aaf80b5..fb1e0b14 100644 --- a/docs/roadmap-resenha.md +++ b/docs/roadmap-resenha.md @@ -73,6 +73,8 @@ fixas por `t('chave')`. O que faltar cai para o inglês sozinho. - Configurações → Conexões (nasceu bilíngue) - Seletor de GIF - Chat: composer e mensagens (placeholder, anexos, responder, reações) +- Barra lateral: servidores, canais e menus de contexto +- Lista de membros (grupos por cargo, carregamento) - Registro de auditoria e Estatísticas (nasceram bilíngues) ### Fila sugerida de tradução diff --git a/packages/web/src/components/layout/ChannelSidebar.tsx b/packages/web/src/components/layout/ChannelSidebar.tsx index ba0809b2..3f9f13eb 100644 --- a/packages/web/src/components/layout/ChannelSidebar.tsx +++ b/packages/web/src/components/layout/ChannelSidebar.tsx @@ -1,4 +1,5 @@ import React, { useState, useRef, useEffect, useCallback, useMemo } from 'react'; +import { useT } from '../../i18n'; import { useNavigate, useLocation } from 'react-router-dom'; import type { Channel } from '@backspace/shared'; import { useSpaceStore, getChannelOrigin, getMyUserIdForOrigin } from '../../stores/spaceStore'; @@ -26,6 +27,7 @@ import { useAudioDevices } from '../../hooks/useAudioDevices'; import { DropdownItem } from '../modals/settingsPanels/_shared/SettingsPickerPrimitives'; export function ChannelSidebar() { + const tr = useT(); const spaces = useSpaceStore((s) => s.spaces); const currentSpaceId = useSpaceStore((s) => s.currentSpaceId); const loadingSpaceId = useSpaceStore((s) => s.loadingSpaceId); @@ -318,7 +320,7 @@ export function ChannelSidebar() { items.push({ key: 'create-channel', type: 'action', - label: 'Create Channel', + label: tr('sidebar.createChannel'), icon: ( @@ -330,7 +332,7 @@ export function ChannelSidebar() { items.push({ key: 'create-category', type: 'action', - label: 'Create Category', + label: tr('sidebar.createCategory'), icon: ( @@ -343,7 +345,7 @@ export function ChannelSidebar() { items.push({ key: 'invite', type: 'action', - label: 'Invite People', + label: tr('sidebar.invitePeople'), icon: ( @@ -355,7 +357,7 @@ export function ChannelSidebar() { items.push({ key: 'settings', type: 'action', - label: 'Space Settings', + label: tr('sidebar.spaceSettings'), icon: ( @@ -373,7 +375,7 @@ export function ChannelSidebar() { { key: 'leave-group', type: 'action', - label: 'Leave Group', + label: tr('sidebar.leaveGroup'), danger: true, icon: ( @@ -453,7 +455,7 @@ export function ChannelSidebar() { - Friends + {tr('sidebar.friends')} {/* Placeholder nav items */} @@ -463,7 +465,7 @@ export function ChannelSidebar() { - Coming Soon + {tr('sidebar.comingSoon')}
- Coming Soon + {tr('sidebar.comingSoon')}
- Direct Messages + {tr('sidebar.directMessages')} )}
)} @@ -845,6 +847,7 @@ function UserAreaPanel({ onDeafenToggle: () => void; onSettingsClick: (tab?: string) => void; }) { + const tr = useT(); const [accountMenuOpen, setAccountMenuOpen] = useState(false); const [openPanel, setOpenPanel] = useState<'input' | 'output' | null>(null); const inputDeviceId = useVoiceStore((s) => s.inputDeviceId); @@ -959,7 +962,7 @@ function UserAreaPanel({ className="w-full px-4 py-3 flex items-center justify-between hover:bg-interactive-hover transition-colors" >
-
Input Device
+
{tr('sidebar.inputDevice')}
{selectedInputLabel}
@@ -1004,7 +1007,7 @@ function UserAreaPanel({ {/* Input Volume */}
-
Input Volume
+
{tr('sidebar.inputVolume')}
onSettingsClick('voice')} className="w-full px-4 py-3 flex items-center justify-between hover:bg-interactive-hover transition-colors" > - Voice Settings + {tr('sidebar.voiceSettings')} @@ -1057,7 +1060,7 @@ function UserAreaPanel({ className="w-full px-4 py-3 flex items-center justify-between hover:bg-interactive-hover transition-colors" >
-
Output Device
+
{tr('sidebar.outputDevice')}
{selectedOutputLabel}
@@ -1102,7 +1105,7 @@ function UserAreaPanel({ {/* Output Volume */}
-
Output Volume
+
{tr('sidebar.outputVolume')}
onSettingsClick('voice')} className="w-full px-4 py-3 flex items-center justify-between hover:bg-interactive-hover transition-colors" > - Voice Settings + {tr('sidebar.voiceSettings')} diff --git a/packages/web/src/components/layout/MemberSidebar.tsx b/packages/web/src/components/layout/MemberSidebar.tsx index 4c84db43..026cd891 100644 --- a/packages/web/src/components/layout/MemberSidebar.tsx +++ b/packages/web/src/components/layout/MemberSidebar.tsx @@ -1,4 +1,5 @@ import React, { useMemo } from 'react'; +import { useT } from '../../i18n'; import type { MemberWithUser, Activity } from '@backspace/shared'; import { useSpaceStore } from '../../stores/spaceStore'; import { useUIStore } from '../../stores/uiStore'; @@ -21,7 +22,7 @@ function getMemberGroup(member: MemberWithUser, ownerId: string | undefined) { const ownerRole = member.roles?.find(r => r.position > 0); return { key: '__owner__', - label: 'OWNER', + label: '__OWNER__', color: ownerRole?.color ?? 'rgb(var(--accent-rose))', position: Infinity, }; @@ -40,7 +41,10 @@ function getMemberGroup(member: MemberWithUser, ownerId: string | undefined) { // No explicit roles — just @everyone return { key: '__online__', - label: 'ONLINE', + // Marcado em vez de traduzido aqui: esta funcao nao e um componente e nao + // pode usar o hook. Nomes de cargo passam adiante sem traducao — sao dados + // do usuario, nao interface. + label: '__ONLINE__', color: undefined, position: -1, }; @@ -111,6 +115,7 @@ export function MemberSidebar() { const currentSpaceId = useSpaceStore((s) => s.currentSpaceId); const loadingSpaceId = useSpaceStore((s) => s.loadingSpaceId); const memberListOpen = useUIStore((s) => s.memberListOpen); + const tr = useT(); const openUserProfile = useUIStore((s) => s.openUserProfile); const userActivities = useActivityStore((s) => s.userActivities); @@ -183,7 +188,7 @@ export function MemberSidebar() { return (
{showMemberSkeleton ? ( -
+
{/* Role group 1 */}
{Array.from({ length: 2 }, (_, i) => ( @@ -207,7 +212,11 @@ export function MemberSidebar() { {roleGroups.map(([key, group]) => (

- {group.label} — {group.members.length} + {group.label === '__ONLINE__' + ? tr('sidebar.groupOnline') + : group.label === '__OWNER__' + ? tr('sidebar.groupOwner') + : group.label} — {group.members.length}

{group.members.map((m) => renderMember(m))}
diff --git a/packages/web/src/i18n/locales/en.ts b/packages/web/src/i18n/locales/en.ts index 7234ce78..d5944cd8 100644 --- a/packages/web/src/i18n/locales/en.ts +++ b/packages/web/src/i18n/locales/en.ts @@ -56,6 +56,28 @@ export const en = { 'accountMenu.copyId': 'Copy User ID', 'accountMenu.copied': 'Copied', + // Sidebar — spaces, channels and members + 'sidebar.friends': 'Friends', + 'sidebar.directMessages': 'Direct Messages', + 'sidebar.noChannels': 'No channels', + 'sidebar.comingSoon': 'Coming Soon', + 'sidebar.loadingChannels': 'Loading channels', + 'sidebar.loadingMembers': 'Loading members', + 'sidebar.groupOnline': 'ONLINE', + 'sidebar.groupOwner': 'OWNER', + 'sidebar.createChannel': 'Create Channel', + 'sidebar.createCategory': 'Create Category', + 'sidebar.categorySettings': 'Category Settings', + 'sidebar.deleteCategory': 'Delete Category', + 'sidebar.invitePeople': 'Invite People', + 'sidebar.spaceSettings': 'Space Settings', + 'sidebar.leaveGroup': 'Leave Group', + 'sidebar.voiceSettings': 'Voice Settings', + 'sidebar.inputDevice': 'Input Device', + 'sidebar.inputVolume': 'Input Volume', + 'sidebar.outputDevice': 'Output Device', + 'sidebar.outputVolume': 'Output Volume', + // Chat — composer and messages 'chat.composer.placeholder': 'Message {channel}', 'chat.composer.attach': 'Attach file', diff --git a/packages/web/src/i18n/locales/pt-BR.ts b/packages/web/src/i18n/locales/pt-BR.ts index 23307bb5..625230eb 100644 --- a/packages/web/src/i18n/locales/pt-BR.ts +++ b/packages/web/src/i18n/locales/pt-BR.ts @@ -55,6 +55,28 @@ export const ptBR: Partial = { 'accountMenu.copyId': 'Copiar ID do usuário', 'accountMenu.copied': 'Copiado', + // Barra lateral — servidores, canais e membros + 'sidebar.friends': 'Amigos', + 'sidebar.directMessages': 'Mensagens diretas', + 'sidebar.noChannels': 'Nenhum canal', + 'sidebar.comingSoon': 'Em breve', + 'sidebar.loadingChannels': 'Carregando canais', + 'sidebar.loadingMembers': 'Carregando membros', + 'sidebar.groupOnline': 'DISPONÍVEIS', + 'sidebar.groupOwner': 'DONO', + 'sidebar.createChannel': 'Criar canal', + 'sidebar.createCategory': 'Criar categoria', + 'sidebar.categorySettings': 'Configurações da categoria', + 'sidebar.deleteCategory': 'Excluir categoria', + 'sidebar.invitePeople': 'Convidar pessoas', + 'sidebar.spaceSettings': 'Configurações do servidor', + 'sidebar.leaveGroup': 'Sair do grupo', + 'sidebar.voiceSettings': 'Configurações de voz', + 'sidebar.inputDevice': 'Dispositivo de entrada', + 'sidebar.inputVolume': 'Volume de entrada', + 'sidebar.outputDevice': 'Dispositivo de saída', + 'sidebar.outputVolume': 'Volume de saída', + // Chat — composer e mensagens 'chat.composer.placeholder': 'Conversar em {channel}', 'chat.composer.attach': 'Anexar arquivo',