diff --git a/public/locales/en.json b/public/locales/en.json index 7a2534b8f..bf8cb08f9 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -2,6 +2,46 @@ "Organisms": { "RoomCommon": { "changed_room_name": " changed room name" + }, + "CreateRoom": { + "chat_room": "Chat Room", + "chat_room_desc": "Messages, photos, and videos.", + "voice_room": "Voice Room", + "voice_room_desc": "Live audio and video conversations." + }, + "ImageViewer": { + "download": "Download" + }, + "Message": { + "open_location": "Open Location", + "thread": "Thread" + }, + "ImageContent": { + "view": "View", + "spoiler": "Spoiler", + "retry": "Retry" + }, + "DeviceVerification": { + "close": "Close", + "accept": "Accept", + "they_match": "They Match", + "okay": "Okay" + }, + "UrlPreview": { + "join_server": "Join Server" + }, + "InviteUser": { + "invite": "Invite" + }, + "UploadBoard": { + "files": "Files", + "send": "Send", + "upload_failed": "Upload Failed" + }, + "PasswordStage": { + "account_password": "Account Password", + "password": "Password", + "invalid_password": "Invalid Password!" } } } diff --git a/src/app/components/DeviceVerification.tsx b/src/app/components/DeviceVerification.tsx index 28130b151..1ef4a094e 100644 --- a/src/app/components/DeviceVerification.tsx +++ b/src/app/components/DeviceVerification.tsx @@ -5,6 +5,7 @@ import { Verifier, } from 'matrix-js-sdk/lib/crypto-api'; import React, { CSSProperties, useCallback, useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; import { VerificationMethod } from 'matrix-js-sdk/lib/types'; import { Box, @@ -51,11 +52,12 @@ function WaitingMessage({ message }: WaitingMessageProps) { type VerificationUnexpectedProps = { message: string; onClose: () => void }; function VerificationUnexpected({ message, onClose }: VerificationUnexpectedProps) { + const { t } = useTranslation(); return ( {message} ); @@ -74,6 +76,7 @@ type VerificationAcceptProps = { onAccept: () => Promise; }; function VerificationAccept({ onAccept }: VerificationAcceptProps) { + const { t } = useTranslation(); const [acceptState, accept] = useAsyncCallback(onAccept); const accepting = acceptState.status === AsyncStatus.Loading; @@ -87,7 +90,7 @@ function VerificationAccept({ onAccept }: VerificationAcceptProps) { before={accepting && } disabled={accepting} > - Accept + {t('Organisms.DeviceVerification.accept')} ); @@ -118,6 +121,7 @@ function AutoVerificationStart({ onStart }: VerificationStartProps) { } function CompareEmoji({ sasData }: { sasData: ShowSasCallbacks }) { + const { t } = useTranslation(); const [confirmState, confirm] = useAsyncCallback(useCallback(() => sasData.confirm(), [sasData])); const confirming = @@ -157,7 +161,7 @@ function CompareEmoji({ sasData }: { sasData: ShowSasCallbacks }) { disabled={confirming} before={confirming && } > - They Match + {t('Organisms.DeviceVerification.they_match')} ); @@ -217,11 +222,12 @@ type VerificationCanceledProps = { onClose: () => void; }; function VerificationCanceled({ onClose }: VerificationCanceledProps) { + const { t } = useTranslation(); return ( Verification has been canceled. ); diff --git a/src/app/components/create-room/CreateRoomTypeSelector.tsx b/src/app/components/create-room/CreateRoomTypeSelector.tsx index b1f69c399..9a2bca714 100644 --- a/src/app/components/create-room/CreateRoomTypeSelector.tsx +++ b/src/app/components/create-room/CreateRoomTypeSelector.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import { useTranslation } from 'react-i18next'; import { Box, Text, Icon, Icons, config, IconSrc } from 'folds'; import { SequenceCard } from '../sequence-card'; import { SettingTile } from '../setting-tile'; @@ -17,6 +18,7 @@ export function CreateRoomTypeSelector({ disabled, getIcon, }: CreateRoomTypeSelectorProps) { + const { t } = useTranslation(); return ( - Chat Room + {t('Organisms.CreateRoom.chat_room')} - - Messages, photos, and videos. + - {t('Organisms.CreateRoom.chat_room_desc')} @@ -61,10 +63,10 @@ export function CreateRoomTypeSelector({ > - Voice Room + {t('Organisms.CreateRoom.voice_room')} - - Live audio and video conversations. + - {t('Organisms.CreateRoom.voice_room_desc')} diff --git a/src/app/components/image-viewer/ImageViewer.tsx b/src/app/components/image-viewer/ImageViewer.tsx index 909751ef7..e4e54ce75 100644 --- a/src/app/components/image-viewer/ImageViewer.tsx +++ b/src/app/components/image-viewer/ImageViewer.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import { useTranslation } from 'react-i18next'; import FileSaver from 'file-saver'; import classNames from 'classnames'; import { Box, Chip, Header, Icon, IconButton, Icons, Text, as } from 'folds'; @@ -15,6 +16,7 @@ export type ImageViewerProps = { export const ImageViewer = as<'div', ImageViewerProps>( ({ className, alt, src, requestClose, ...props }, ref) => { + const { t } = useTranslation(); const { zoom, zoomIn, zoomOut, setZoom } = useZoom(0.2); const { pan, cursor, onMouseDown } = usePan(zoom !== 1); @@ -69,7 +71,7 @@ export const ImageViewer = as<'div', ImageViewerProps>( radii="300" before={} > - Download + {t('Organisms.ImageViewer.download')} diff --git a/src/app/components/invite-user-prompt/InviteUserPrompt.tsx b/src/app/components/invite-user-prompt/InviteUserPrompt.tsx index 93aaa0efd..098ce0b63 100644 --- a/src/app/components/invite-user-prompt/InviteUserPrompt.tsx +++ b/src/app/components/invite-user-prompt/InviteUserPrompt.tsx @@ -7,6 +7,7 @@ import React, { useRef, useState, } from 'react'; +import { useTranslation } from 'react-i18next'; import { Overlay, OverlayBackdrop, @@ -66,6 +67,7 @@ type InviteUserProps = { requestClose: () => void; }; export function InviteUserPrompt({ room, requestClose }: InviteUserProps) { + const { t } = useTranslation(); const mx = useMatrixClient(); const modalStyle = useModalStyle(560); const alive = useAlive(); @@ -194,7 +196,7 @@ export function InviteUserPrompt({ room, requestClose }: InviteUserProps) { > - Invite + {t('Organisms.InviteUser.invite')} @@ -351,7 +353,7 @@ export function InviteUserPrompt({ room, requestClose }: InviteUserProps) { disabled={!validUserId || inviting} before={inviting && } > - Invite + {t('Organisms.InviteUser.invite')} diff --git a/src/app/components/message/MsgTypeRenderers.tsx b/src/app/components/message/MsgTypeRenderers.tsx index 8f557c7d9..d29a5056d 100644 --- a/src/app/components/message/MsgTypeRenderers.tsx +++ b/src/app/components/message/MsgTypeRenderers.tsx @@ -1,4 +1,5 @@ import React, { CSSProperties, ReactNode, useEffect, useRef, useState } from 'react'; +import { useTranslation } from 'react-i18next'; import { Box, Button, config, Icon, Icons, Text, color, toRem } from 'folds'; import { IContent } from 'matrix-js-sdk'; import { JUMBO_EMOJI_REG, URL_REG } from '../../utils/regex'; @@ -507,6 +508,7 @@ type MLocationProps = { content: IContent; }; export function MLocation({ content }: MLocationProps) { + const { t } = useTranslation(); const geoUri = content.geo_uri; if (typeof geoUri !== 'string') return ; const location = parseGeoUri(geoUri); @@ -549,7 +551,7 @@ export function MLocation({ content }: MLocationProps) { radii="300" before={} > - Open Location + {t('Organisms.Message.open_location')} ); diff --git a/src/app/components/message/Reply.tsx b/src/app/components/message/Reply.tsx index 66d6ebf26..5d2a65899 100644 --- a/src/app/components/message/Reply.tsx +++ b/src/app/components/message/Reply.tsx @@ -1,6 +1,7 @@ import { Box, Icon, Icons, Text, as, color, toRem } from 'folds'; import { EventTimelineSet, Room } from 'matrix-js-sdk'; import React, { MouseEventHandler, ReactNode, useCallback, useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; import classNames from 'classnames'; import { getMemberDisplayName, trimReplyFromBody } from '../../utils/room'; import { getMxIdLocalPart } from '../../utils/matrix'; @@ -37,19 +38,22 @@ export const ReplyLayout = as<'div', ReplyLayoutProps>( ), ); -export const ThreadIndicator = as<'div'>(({ ...props }, ref) => ( - - - Thread - -)); +export const ThreadIndicator = as<'div'>(({ ...props }, ref) => { + const { t } = useTranslation(); + return ( + + + {t('Organisms.Message.thread')} + + ); +}); type ReplyProps = { room: Room; diff --git a/src/app/components/message/content/ImageContent.tsx b/src/app/components/message/content/ImageContent.tsx index 203d06abc..65ab4655c 100644 --- a/src/app/components/message/content/ImageContent.tsx +++ b/src/app/components/message/content/ImageContent.tsx @@ -1,4 +1,5 @@ import React, { ReactNode, useCallback, useEffect, useRef, useState } from 'react'; +import { useTranslation } from 'react-i18next'; import { Badge, Box, @@ -81,6 +82,7 @@ export const ImageContent = as<'div', ImageContentProps>( const useAuthentication = useMediaAuthentication(); const blurHash = validBlurHash(info?.[MATRIX_BLUR_HASH_PROPERTY_NAME]); + const { t } = useTranslation(); const [load, setLoad] = useState(false); const [error, setError] = useState(false); const [viewer, setViewer] = useState(false); @@ -168,7 +170,7 @@ export const ImageContent = as<'div', ImageContentProps>( onClick={loadSrc} before={} > - View + {t('Organisms.ImageContent.view')} )} @@ -212,7 +214,7 @@ export const ImageContent = as<'div', ImageContentProps>( } }} > - Spoiler + {t('Organisms.ImageContent.spoiler')} )} @@ -247,7 +249,7 @@ export const ImageContent = as<'div', ImageContentProps>( onClick={handleRetry} before={} > - Retry + {t('Organisms.ImageContent.retry')} )} diff --git a/src/app/components/uia-stages/PasswordStage.tsx b/src/app/components/uia-stages/PasswordStage.tsx index d408dacb2..445049e68 100644 --- a/src/app/components/uia-stages/PasswordStage.tsx +++ b/src/app/components/uia-stages/PasswordStage.tsx @@ -1,5 +1,6 @@ import { Box, Button, color, config, Dialog, Header, Icon, IconButton, Icons, Text } from 'folds'; import React, { FormEventHandler } from 'react'; +import { useTranslation } from 'react-i18next'; import { AuthType } from 'matrix-js-sdk'; import { StageComponentProps } from './types'; import { ErrorCode } from '../../cs-errorcode'; @@ -13,6 +14,7 @@ export function PasswordStage({ }: StageComponentProps & { userId: string; }) { + const { t } = useTranslation(); const { errorCode, error, session } = stageData; const handleFormSubmit: FormEventHandler = (evt) => { @@ -44,7 +46,7 @@ export function PasswordStage({ > - Account Password + {t('Organisms.PasswordStage.account_password')} @@ -64,7 +66,7 @@ export function PasswordStage({ password. - Password + {t('Organisms.PasswordStage.password')} {errorCode && ( @@ -72,7 +74,7 @@ export function PasswordStage({ {errorCode === ErrorCode.M_FORBIDDEN - ? 'Invalid Password!' + ? t('Organisms.PasswordStage.invalid_password') : `${errorCode}: ${error}`} diff --git a/src/app/components/upload-board/UploadBoard.tsx b/src/app/components/upload-board/UploadBoard.tsx index 03690782f..b3a95ec0f 100644 --- a/src/app/components/upload-board/UploadBoard.tsx +++ b/src/app/components/upload-board/UploadBoard.tsx @@ -1,4 +1,5 @@ import React, { MutableRefObject, ReactNode, useImperativeHandle, useRef } from 'react'; +import { useTranslation } from 'react-i18next'; import { Badge, Box, Chip, Header, Icon, Icons, Spinner, Text, as, percent } from 'folds'; import classNames from 'classnames'; import { useAtomValue } from 'jotai'; @@ -43,6 +44,7 @@ export function UploadBoardHeader({ onSend, imperativeHandlerRef, }: UploadBoardHeaderProps) { + const { t } = useTranslation(); const sendingRef = useRef(false); const uploads = useAtomValue(uploadFamilyObserverAtom); @@ -88,7 +90,7 @@ export function UploadBoardHeader({ gap="100" > - Files + {t('Organisms.UploadBoard.files')} {isSuccess && ( @@ -100,12 +102,12 @@ export function UploadBoardHeader({ outlined after={} > - Send + {t('Organisms.UploadBoard.send')} )} {isError && !open && ( - Upload Failed + {t('Organisms.UploadBoard.upload_failed')} )} {!isSuccess && !isError && !open && ( diff --git a/src/app/components/url-preview/UrlPreviewCard.tsx b/src/app/components/url-preview/UrlPreviewCard.tsx index 91f0bad9c..d8ac48972 100644 --- a/src/app/components/url-preview/UrlPreviewCard.tsx +++ b/src/app/components/url-preview/UrlPreviewCard.tsx @@ -1,4 +1,5 @@ import React, { useCallback, useEffect, useRef, useState } from 'react'; +import { useTranslation } from 'react-i18next'; import { IPreviewUrlResponse } from 'matrix-js-sdk'; import { Box, Icon, IconButton, Icons, Scroll, Spinner, Text, as, color, config } from 'folds'; import { ImageOverlay } from '../ImageOverlay'; @@ -1343,6 +1344,7 @@ function WikipediaCard({ url, prev }: { url: string; prev: IPreviewUrlResponse } } function DiscordCard({ url, prev }: { url: string; prev: IPreviewUrlResponse }) { + const { t } = useTranslation(); const title = prev['og:title'] ?? ''; const description = prev['og:description'] ?? ''; const iconUrl = (prev['og:image'] as string | undefined) ?? ''; @@ -1383,7 +1385,9 @@ function DiscordCard({ url, prev }: { url: string; prev: IPreviewUrlResponse }) priority="300" > - Join Server + + {t('Organisms.UrlPreview.join_server')} + {title && (