From f4e94a48ccdc40ed5df7eb1d0776c05dc575cafb Mon Sep 17 00:00:00 2001 From: heaven Date: Tue, 14 Apr 2026 01:59:41 +0300 Subject: [PATCH] localize Explore Community --- public/locales/en.json | 47 +++++++++++++++++++ public/locales/ru.json | 47 +++++++++++++++++++ src/app/components/room-card/RoomCard.tsx | 21 +++++---- src/app/pages/client/explore/Explore.tsx | 21 +++++---- src/app/pages/client/explore/Featured.tsx | 12 +++-- src/app/pages/client/explore/Server.tsx | 51 ++++++++++++--------- src/app/pages/client/sidebar/ExploreTab.tsx | 4 +- 7 files changed, 157 insertions(+), 46 deletions(-) diff --git a/public/locales/en.json b/public/locales/en.json index 5bd20971..98695262 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -507,5 +507,52 @@ "unverified_device": "Unverified Device", "unverified_devices": "Unverified Devices" + }, + + "Explore": { + "explore_community": "Explore Community", + + "add_server": "Add Server", + "add_server_desc": "Add server name to explore public communities.", + "server_name": "Server Name", + "failed_load_public_rooms": "Failed to load public rooms. Please try again.", + "view": "View", + "featured": "Featured", + "servers": "Servers", + + "featured_by_client": "Featured by Client", + "featured_by_client_desc": "Public rooms and spaces hand-picked by this client.", + "featured_spaces": "Featured Spaces", + "featured_rooms": "Featured Rooms", + "no_featured": "No featured rooms or spaces yet.", + + "search": "Search", + "search_placeholder": "Search for keyword", + "clear": "Clear", + "enter": "Enter", + "protocols": "Protocols", + "presets": "Presets", + "custom_limit": "Custom Limit", + "per_page_limit": "Per Page Item Limit", + "change_limit": "Change Limit", + "page_limit": "Page Limit: {{limit}}", + "results_for": "Results for \"{{term}}\"", + "popular_communities": "Popular Communities", + "filter_all": "All", + "filter_spaces": "Spaces", + "filter_rooms": "Rooms", + "previous_page": "Previous Page", + "next_page": "Next Page", + "no_communities": "No communities found!", + + "space_badge": "Space", + "members_count": "{{count}} Members", + "join": "Join", + "joining": "Joining", + "retry": "Retry", + "join_error": "Join Error", + "join_error_unknown": "Failed to join. Unknown Error.", + "view_error": "View Error", + "cancel": "Cancel" } } diff --git a/public/locales/ru.json b/public/locales/ru.json index 2361c3de..a506fb2b 100644 --- a/public/locales/ru.json +++ b/public/locales/ru.json @@ -509,5 +509,52 @@ "unverified_device": "Неподтверждённое устройство", "unverified_devices": "Неподтверждённые устройства" + }, + + "Explore": { + "explore_community": "Обзор сообществ", + + "add_server": "Добавить сервер", + "add_server_desc": "Укажите имя сервера для обзора публичных сообществ.", + "server_name": "Имя сервера", + "failed_load_public_rooms": "Не удалось загрузить публичные комнаты. Попробуйте ещё раз.", + "view": "Открыть", + "featured": "Рекомендуемые", + "servers": "Серверы", + + "featured_by_client": "Рекомендации клиента", + "featured_by_client_desc": "Подборка публичных комнат и пространств от этого клиента.", + "featured_spaces": "Рекомендуемые пространства", + "featured_rooms": "Рекомендуемые комнаты", + "no_featured": "Рекомендуемых комнат и пространств пока нет.", + + "search": "Поиск", + "search_placeholder": "Поиск по ключевому слову", + "clear": "Очистить", + "enter": "Найти", + "protocols": "Протоколы", + "presets": "Готовые значения", + "custom_limit": "Своё значение", + "per_page_limit": "Элементов на странице", + "change_limit": "Применить", + "page_limit": "На странице: {{limit}}", + "results_for": "Результаты для «{{term}}»", + "popular_communities": "Популярные сообщества", + "filter_all": "Все", + "filter_spaces": "Пространства", + "filter_rooms": "Комнаты", + "previous_page": "Предыдущая", + "next_page": "Следующая", + "no_communities": "Сообщества не найдены!", + + "space_badge": "Пространство", + "members_count": "{{count}} участников", + "join": "Присоединиться", + "joining": "Вступление…", + "retry": "Повторить", + "join_error": "Не удалось вступить", + "join_error_unknown": "Не удалось присоединиться. Неизвестная ошибка.", + "view_error": "Подробности", + "cancel": "Отмена" } } diff --git a/src/app/components/room-card/RoomCard.tsx b/src/app/components/room-card/RoomCard.tsx index 34a7e24b..38b19fa3 100644 --- a/src/app/components/room-card/RoomCard.tsx +++ b/src/app/components/room-card/RoomCard.tsx @@ -19,6 +19,7 @@ import { } from 'folds'; import classNames from 'classnames'; import FocusTrap from 'focus-trap-react'; +import { useTranslation } from 'react-i18next'; import * as css from './style.css'; import { RoomAvatar } from '../room-avatar'; import { getMxIdLocalPart, mxcUrlToHttp } from '../../utils/matrix'; @@ -94,6 +95,7 @@ function ErrorDialog({ message: string; children: (openError: () => void) => ReactNode; }) { + const { t } = useTranslation(); const [viewError, setViewError] = useState(false); const closeError = () => setViewError(false); const openError = () => setViewError(true); @@ -120,7 +122,7 @@ function ErrorDialog({ @@ -161,6 +163,7 @@ export const RoomCard = as<'div', RoomCardProps>( }, ref ) => { + const { t } = useTranslation(); const mx = useMatrixClient(); const useAuthentication = useMediaAuthentication(); const joinedRoomId = useJoinedRoomId(allRooms, roomIdOrAlias); @@ -224,7 +227,7 @@ export const RoomCard = as<'div', RoomCardProps>( {(roomType === RoomType.Space || joinedRoom?.isSpaceRoom()) && ( - Space + {t('Explore.space_badge')} )} @@ -252,7 +255,7 @@ export const RoomCard = as<'div', RoomCardProps>( {typeof joinedMemberCount === 'number' && ( - {`${millify(joinedMemberCount)} Members`} + {t('Explore.members_count', { count: millify(joinedMemberCount) })} )} {typeof joinedRoomId === 'string' && ( @@ -263,7 +266,7 @@ export const RoomCard = as<'div', RoomCardProps>( size="300" > - View + {t('Explore.view')} )} @@ -276,7 +279,7 @@ export const RoomCard = as<'div', RoomCardProps>( before={joining && } > - {joining ? 'Joining' : 'Join'} + {joining ? t('Explore.joining') : t('Explore.join')} )} @@ -290,12 +293,12 @@ export const RoomCard = as<'div', RoomCardProps>( size="300" > - Retry + {t('Explore.retry')} {(openError) => ( )} diff --git a/src/app/pages/client/explore/Explore.tsx b/src/app/pages/client/explore/Explore.tsx index dae83166..47247931 100644 --- a/src/app/pages/client/explore/Explore.tsx +++ b/src/app/pages/client/explore/Explore.tsx @@ -1,6 +1,7 @@ import React, { FormEventHandler, useCallback, useRef, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import FocusTrap from 'focus-trap-react'; +import { useTranslation } from 'react-i18next'; import { Avatar, Box, @@ -39,6 +40,7 @@ import { PageNav, PageNavContent, PageNavHeader } from '../../../components/page import { stopPropagation } from '../../../utils/keyboard'; export function AddServer() { + const { t } = useTranslation(); const mx = useMatrixClient(); const navigate = useNavigate(); const [dialog, setDialog] = useState(false); @@ -94,7 +96,7 @@ export function AddServer() { size="500" > - Add Server + {t('Explore.add_server')} setDialog(false)} radii="300"> @@ -107,13 +109,13 @@ export function AddServer() { direction="Column" gap="400" > - Add server name to explore public communities. + {t('Explore.add_server_desc')} - Server Name + {t('Explore.server_name')} {exploreState.status === AsyncStatus.Error && ( - Failed to load public rooms. Please try again. + {t('Explore.failed_load_public_rooms')} )} @@ -132,7 +134,7 @@ export function AddServer() { */} @@ -148,7 +150,7 @@ export function AddServer() { onClick={() => setDialog(true)} > - Add Server + {t('Explore.add_server')} @@ -156,6 +158,7 @@ export function AddServer() { } export function Explore() { + const { t } = useTranslation(); const mx = useMatrixClient(); useNavToActivePathMapper('explore'); const userId = mx.getUserId(); @@ -173,7 +176,7 @@ export function Explore() { - Explore Community + {t('Explore.explore_community')} @@ -191,7 +194,7 @@ export function Explore() { - Featured + {t('Explore.featured')} @@ -229,7 +232,7 @@ export function Explore() { - Servers + {t('Explore.servers')} {servers.map((server) => ( diff --git a/src/app/pages/client/explore/Featured.tsx b/src/app/pages/client/explore/Featured.tsx index f056cbb5..ee4d4e38 100644 --- a/src/app/pages/client/explore/Featured.tsx +++ b/src/app/pages/client/explore/Featured.tsx @@ -1,5 +1,6 @@ import React from 'react'; import { Box, Icon, IconButton, Icons, Scroll, Text } from 'folds'; +import { useTranslation } from 'react-i18next'; import { useAtomValue } from 'jotai'; import { useClientConfig } from '../../../hooks/useClientConfig'; import { RoomCard, RoomCardGrid } from '../../../components/room-card'; @@ -20,6 +21,7 @@ import { ScreenSize, useScreenSizeContext } from '../../../hooks/useScreenSize'; import { BackRouteHandler } from '../../../components/BackRouteHandler'; export function FeaturedRooms() { + const { t } = useTranslation(); const { featuredCommunities } = useClientConfig(); const { rooms, spaces } = featuredCommunities ?? {}; const allRooms = useAtomValue(allRoomsAtom); @@ -49,14 +51,14 @@ export function FeaturedRooms() { } - title="Featured by Client" - subTitle="Find and explore public rooms and spaces featured by client provider." + title={t('Explore.featured_by_client')} + subTitle={t('Explore.featured_by_client_desc')} /> {spaces && spaces.length > 0 && ( - Featured Spaces + {t('Explore.featured_spaces')} {spaces.map((roomIdOrAlias) => ( @@ -85,7 +87,7 @@ export function FeaturedRooms() { )} {rooms && rooms.length > 0 && ( - Featured Rooms + {t('Explore.featured_rooms')} {rooms.map((roomIdOrAlias) => ( @@ -123,7 +125,7 @@ export function FeaturedRooms() { > - No rooms or spaces featured by client provider. + {t('Explore.no_featured')} )} diff --git a/src/app/pages/client/explore/Server.tsx b/src/app/pages/client/explore/Server.tsx index 48f267cc..eaeb35f7 100644 --- a/src/app/pages/client/explore/Server.tsx +++ b/src/app/pages/client/explore/Server.tsx @@ -29,6 +29,7 @@ import { } from 'folds'; import { useNavigate, useParams, useSearchParams } from 'react-router-dom'; import FocusTrap from 'focus-trap-react'; +import { useTranslation } from 'react-i18next'; import { useAtomValue } from 'jotai'; import { useQuery } from '@tanstack/react-query'; import { MatrixClient, Method, RoomType } from 'matrix-js-sdk'; @@ -62,24 +63,26 @@ type RoomTypeFilter = { title: string; value: string | undefined; }; -const useRoomTypeFilters = (): RoomTypeFilter[] => - useMemo( +const useRoomTypeFilters = (): RoomTypeFilter[] => { + const { t } = useTranslation(); + return useMemo( () => [ { - title: 'All', + title: t('Explore.filter_all'), value: undefined, }, { - title: 'Spaces', + title: t('Explore.filter_spaces'), value: RoomType.Space, }, { - title: 'Rooms', + title: t('Explore.filter_rooms'), value: 'null', }, ], - [] + [t] ); +}; const FALLBACK_ROOMS_LIMIT = 24; @@ -91,6 +94,7 @@ type SearchProps = { onReset: () => void; }; function Search({ active, loading, searchInputRef, onSearch, onReset }: SearchProps) { + const { t } = useTranslation(); const handleSearchSubmit: FormEventHandler = (evt) => { evt.preventDefault(); const { searchInput } = evt.target as HTMLFormElement & { @@ -106,14 +110,14 @@ function Search({ active, loading, searchInputRef, onSearch, onReset }: SearchPr return ( - Search + {t('Explore.search')} @@ -132,11 +136,11 @@ function Search({ active, loading, searchInputRef, onSearch, onReset }: SearchPr after={} onClick={onReset} > - Clear + {t('Explore.clear')} ) : ( - Enter + {t('Explore.enter')} ) } @@ -153,6 +157,7 @@ function ThirdPartyProtocolsSelector({ instanceId?: string; onChange: (instanceId?: string) => void; }) { + const { t } = useTranslation(); const mx = useMatrixClient(); const [menuAnchor, setMenuAnchor] = useState(); @@ -196,7 +201,7 @@ function ThirdPartyProtocolsSelector({ style={{ padding: config.space.S100, minWidth: toRem(100) }} > - Protocols + {t('Explore.protocols')} void; }; function LimitButton({ limit, onLimitChange }: LimitButtonProps) { + const { t } = useTranslation(); const [menuAnchor, setMenuAnchor] = useState(); const handleLimitSubmit: FormEventHandler = (evt) => { @@ -288,7 +294,7 @@ function LimitButton({ limit, onLimitChange }: LimitButtonProps) { - Presets + {t('Explore.presets')} setLimit('24')} radii="Pill"> 24 @@ -303,7 +309,7 @@ function LimitButton({ limit, onLimitChange }: LimitButtonProps) { - Custom Limit + {t('Explore.custom_limit')} @@ -334,13 +340,14 @@ function LimitButton({ limit, onLimitChange }: LimitButtonProps) { variant="SurfaceVariant" after={} > - {`Page Limit: ${limit}`} + {t('Explore.page_limit', { limit })} ); } export function PublicRooms() { + const { t } = useTranslation(); const { server } = useParams(); const mx = useMatrixClient(); const userId = mx.getUserId(); @@ -488,7 +495,7 @@ export function PublicRooms() { {screenSize !== ScreenSize.Mobile && } - Search + {t('Explore.search')} @@ -532,9 +539,9 @@ export function PublicRooms() { {isSearch ? ( - {`Results for "${serverSearchParams.term}"`} + {t('Explore.results_for', { term: serverSearchParams.term })} ) : ( - Popular Communities + {t('Explore.popular_communities')} )} {roomTypeFilters.map((filter) => ( @@ -624,7 +631,7 @@ export function PublicRooms() { disabled={!data.prev_batch} > - Previous Page + {t('Explore.previous_page')} @@ -635,7 +642,7 @@ export function PublicRooms() { disabled={!data.next_batch} > - Next Page + {t('Explore.next_page')} @@ -651,7 +658,7 @@ export function PublicRooms() { > - No communities found! + {t('Explore.no_communities')} ))} diff --git a/src/app/pages/client/sidebar/ExploreTab.tsx b/src/app/pages/client/sidebar/ExploreTab.tsx index a45b5830..88ec6a7a 100644 --- a/src/app/pages/client/sidebar/ExploreTab.tsx +++ b/src/app/pages/client/sidebar/ExploreTab.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { Icon, Icons } from 'folds'; import { useNavigate } from 'react-router-dom'; +import { useTranslation } from 'react-i18next'; import { useAtomValue } from 'jotai'; import { SidebarAvatar, SidebarItem, SidebarItemTooltip } from '../../../components/sidebar'; import { useExploreSelected } from '../../../hooks/router/useExploreSelected'; @@ -17,6 +18,7 @@ import { ScreenSize, useScreenSizeContext } from '../../../hooks/useScreenSize'; import { useNavToActivePathAtom } from '../../../state/hooks/navToActivePath'; export function ExploreTab() { + const { t } = useTranslation(); const mx = useMatrixClient(); const screenSize = useScreenSizeContext(); const clientConfig = useClientConfig(); @@ -52,7 +54,7 @@ export function ExploreTab() { return ( - + {(triggerRef) => (