From 4c7356903d523335e720d659dfa591921fe90ac6 Mon Sep 17 00:00:00 2001 From: Rye Date: Sat, 23 May 2026 01:34:26 +0200 Subject: [PATCH 01/23] add i18n-ally configuration for localization support [no ci] --- .vscode/extensions.json | 6 +++++- .vscode/settings.json | 19 ++++++++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 434432fea2..34b9eba949 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,3 +1,7 @@ { - "recommendations": ["webpro.vscode-knip", "oxc.oxc-vscode"] + "recommendations": [ + "webpro.vscode-knip", + "oxc.oxc-vscode", + "lokalise.i18n-ally" + ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index 60018f71d7..82961cddb6 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -14,5 +14,22 @@ "editor.defaultFormatter": "oxc.oxc-vscode" }, "nixEnvSelector.nixFile": "${workspaceFolder}/flake.nix", - "nixEnvSelector.useFlakes": true + "nixEnvSelector.useFlakes": true, + "i18n-ally.localesPaths": [ + "public/locales" + ], + "i18n-ally.dirStructure": "auto", + "i18n-ally.enabledFrameworks": [ + "react", + "react-i18next" + ], + "i18n-ally.extract.keyMaxLength": 75, + "i18n-ally.extract.targetPickingStrategy": "most-similar", + "i18n-ally.keystyle": "nested", + "i18n-ally.extract.keygenStyle": "snake_case", + "i18n-ally.extract.ignoredByFiles": { + "src/app/components/DeviceVerificationSetup.tsx": [ + "Column" + ] + } } From a4eb7a3a0c3c15702ad4c8913ea0b20143d3d7b4 Mon Sep 17 00:00:00 2001 From: Rye Date: Sat, 23 May 2026 01:34:54 +0200 Subject: [PATCH 02/23] integrate localization for device verification setup messages --- public/locales/en.json | 30 +++++++++++++ .../components/DeviceVerificationSetup.tsx | 45 +++++++++---------- 2 files changed, 51 insertions(+), 24 deletions(-) diff --git a/public/locales/en.json b/public/locales/en.json index 7a2534b8f7..27af768833 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -3,5 +3,35 @@ "RoomCommon": { "changed_room_name": " changed room name" } + }, + "Settings": { + "device_verification_setup": { + "error_uia_action_without_data": "Unexpected Error! UIA action is perform without data.", + "authentication_failed_failed_to_setup_device_verification": "Authentication failed! Failed to setup device verification.", + "unexpected_error_crypto_module_not_found": "Unexpected Error! Crypto module not found!", + "unexpected_error_failed_to_create_recovery_key": "Unexpected Error! Failed to create recovery key.", + "generate_a": "Generate a", + "for_verifying_identity_if_you_do_not_have_access_to_other_devices_additiona": "for verifying identity if you do not have access to other devices. Additionally, setup a passphrase as a memorable alternative.", + "authentication_steps_to_perform_this_action_are_not_supported_by_client": "Authentication steps to perform this action are not supported by client.", + "store_the_recovery_key_in_a_safe_place_for_future_use_as_you_will_need_it_t": "Store the Recovery Key in a safe place for future use, as you will need it to verify your identity if you do not have access to other devices.", + "setup_device_verification": "Setup Device Verification", + "reset_device_verification": "Reset Device Verification", + "resetting_device_verification_is_permanent": "Resetting device verification is permanent.", + "anyone_you_have_verified_with_will_see_security_alerts_and_your_encryption": "Anyone you have verified with will see security alerts and your encryption backup will be lost. You almost certainly do not want to do this, unless you have lost", + "and_every_device_you_can_verify_from": "and every device you can verify from." + } + }, + "General": { + "continue": "Continue", + "passphrase": "Passphrase", + "optional": "optional", + "unexpected_error": "Unexpected Error!", + "recovery_key": "Recovery Key", + "download": "Download", + "copy": "Copy", + "hide": "Hide", + "show": "Show", + "recovery_passphrase": "Recovery Passphrase", + "reset": "Reset" } } diff --git a/src/app/components/DeviceVerificationSetup.tsx b/src/app/components/DeviceVerificationSetup.tsx index 34bfa8d90e..19902b4f05 100644 --- a/src/app/components/DeviceVerificationSetup.tsx +++ b/src/app/components/DeviceVerificationSetup.tsx @@ -27,6 +27,7 @@ import { useAlive } from '$hooks/useAlive'; import { PasswordInput } from './password-input'; import { ActionUIA, ActionUIAFlowsLoader } from './ActionUIA'; import { UseStateProvider } from './UseStateProvider'; +import { t } from 'i18next'; type UIACallback = ( authDict: AuthDict | null @@ -82,7 +83,7 @@ function SetupVerification({ onComplete }: Readonly) { const handleAction = useCallback( async (authDict: AuthDict) => { if (!uiaAction) { - throw new Error('Unexpected Error! UIA action is perform without data.'); + throw new Error(t('Settings.device_verification_setup.error_uia_action_without_data')); } if (alive()) { setNextAuthData(null); @@ -125,7 +126,7 @@ function SetupVerification({ onComplete }: Readonly) { if (alive()) { setUIAAction(action); } else { - reject(new Error('Authentication failed! Failed to setup device verification.')); + reject(new Error(t('Settings.device_verification_setup.authentication_failed_failed_to_setup_device_verification'))); } return; } @@ -139,11 +140,11 @@ function SetupVerification({ onComplete }: Readonly) { useCallback( async (passphrase) => { const crypto = mx.getCrypto(); - if (!crypto) throw new Error('Unexpected Error! Crypto module not found!'); + if (!crypto) throw new Error(t('Settings.device_verification_setup.unexpected_error_crypto_module_not_found')); const recoveryKeyData = await crypto.createRecoveryKeyFromPassphrase(passphrase); if (!recoveryKeyData.encodedPrivateKey) { - throw new Error('Unexpected Error! Failed to create recovery key.'); + throw new Error(t('Settings.device_verification_setup.unexpected_error_failed_to_create_recovery_key')); } clearSecretStorageKeys(); @@ -184,11 +185,10 @@ function SetupVerification({ onComplete }: Readonly) { return ( - Generate a Recovery Key for verifying identity if you do not have access to other - devices. Additionally, setup a passphrase as a memorable alternative. + {t('Settings.device_verification_setup.generate_a')} {t('General.recovery_key')} {t('Settings.device_verification_setup.for_verifying_identity_if_you_do_not_have_access_to_other_devices_additiona')} - Passphrase (Optional) + {t('General.passphrase')} ({t('General.optional')}) {setupState.status === AsyncStatus.Error && ( - {setupState.error ? setupState.error.message : 'Unexpected Error!'} + {setupState.error ? setupState.error.message : t('General.unexpected_error')} )} {nextAuthData !== null && uiaAction && ( @@ -208,7 +208,7 @@ function SetupVerification({ onComplete }: Readonly) { authData={nextAuthData ?? uiaAction.authData} unsupported={() => ( - Authentication steps to perform this action are not supported by client. + {t('Settings.device_verification_setup.authentication_steps_to_perform_this_action_are_not_supported_by_client')} )} > @@ -248,11 +248,10 @@ function RecoveryKeyDisplay({ recoveryKey }: Readonly) return ( - Store the Recovery Key in a safe place for future use, as you will need it to verify your - identity if you do not have access to other devices. + {t('Settings.device_verification_setup.store_the_recovery_key_in_a_safe_place_for_future_use_as_you_will_need_it_t')} - Recovery Key + {t('General.recovery_key')} ) {safeToDisplayKey} setShow(!show)} variant="Secondary" radii="Pill"> - {show ? 'Hide' : 'Show'} + {show ? t('General.hide') : t('General.show')} @@ -301,7 +300,7 @@ export const DeviceVerificationSetup = forwardRef - Setup Device Verification + {t('Settings.device_verification_setup.setup_device_verification')} @@ -336,7 +335,7 @@ export const DeviceVerificationReset = forwardRef - Reset Device Verification + {t('Settings.device_verification_setup.reset_device_verification')} @@ -358,16 +357,14 @@ export const DeviceVerificationReset = forwardRef βœ‹πŸ§‘β€πŸš’πŸ€š - Resetting device verification is permanent. + {t('Settings.device_verification_setup.resetting_device_verification_is_permanent')} - Anyone you have verified with will see security alerts and your encryption backup - will be lost. You almost certainly do not want to do this, unless you have lost{' '} - Recovery Key or Recovery Passphrase and every device you can verify - from. + {t('Settings.device_verification_setup.anyone_you_have_verified_with_will_see_security_alerts_and_your_encryption')}{' '} + {t('General.recovery_key')} or {t('General.recovery_passphrase')} {t('Settings.device_verification_setup.and_every_device_you_can_verify_from')} )} From 1cacfd3c6ec6a0bf32f5b234fb5f804e78a14933 Mon Sep 17 00:00:00 2001 From: Rye Date: Sat, 23 May 2026 01:41:34 +0200 Subject: [PATCH 03/23] update device verification messages for localization support --- public/locales/en.json | 22 +++++++++-- src/app/components/DeviceVerification.tsx | 37 ++++++++++--------- .../components/DeviceVerificationSetup.tsx | 24 ++++++------ 3 files changed, 50 insertions(+), 33 deletions(-) diff --git a/public/locales/en.json b/public/locales/en.json index 27af768833..a10128e2fb 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -5,7 +5,7 @@ } }, "Settings": { - "device_verification_setup": { + "device_verification": { "error_uia_action_without_data": "Unexpected Error! UIA action is perform without data.", "authentication_failed_failed_to_setup_device_verification": "Authentication failed! Failed to setup device verification.", "unexpected_error_crypto_module_not_found": "Unexpected Error! Crypto module not found!", @@ -18,7 +18,20 @@ "reset_device_verification": "Reset Device Verification", "resetting_device_verification_is_permanent": "Resetting device verification is permanent.", "anyone_you_have_verified_with_will_see_security_alerts_and_your_encryption": "Anyone you have verified with will see security alerts and your encryption backup will be lost. You almost certainly do not want to do this, unless you have lost", - "and_every_device_you_can_verify_from": "and every device you can verify from." + "and_every_device_you_can_verify_from": "and every device you can verify from.", + "please_accept_the_request_from_other_device": "Please accept the request from other device.", + "click_accept_to_start_the_verification_process": "Click accept to start the verification process.", + "waiting_for_request_to_be_accepted": "Waiting for request to be accepted...", + "confirm_the_emoji_below_are_displayed_on_both_devices_in_the_same_order": "Confirm the emoji below are displayed on both devices, in the same order:", + "they_match": "They Match", + "do_not_match": "Do not Match", + "starting_verification_using_emoji_comparison": "Starting verification using emoji comparison...", + "your_device_is_verified": "Your device is verified.", + "verification_has_been_canceled": "Verification has been canceled.", + "device_verification": "Device Verification", + "unexpected_error_verification_is_started_but_verifier_is_missing": "Unexpected Error! Verification is started but verifier is missing.", + "verification_request_has_been_accepted": "Verification request has been accepted.", + "waiting_for_the_response_from_other_device": "Waiting for the response from other device..." } }, "General": { @@ -32,6 +45,9 @@ "hide": "Hide", "show": "Show", "recovery_passphrase": "Recovery Passphrase", - "reset": "Reset" + "reset": "Reset", + "close": "Close", + "accept": "Accept", + "okay": "Okay" } } diff --git a/src/app/components/DeviceVerification.tsx b/src/app/components/DeviceVerification.tsx index aedfe8e1ea..af7e256d78 100644 --- a/src/app/components/DeviceVerification.tsx +++ b/src/app/components/DeviceVerification.tsx @@ -27,6 +27,7 @@ import { } from '$hooks/useVerificationRequest'; import { AsyncStatus, useAsyncCallback } from '$hooks/useAsyncCallback'; import { ContainerColor } from '$styles/ContainerColor.css'; +import { t } from 'i18next'; const DialogHeaderStyles: CSSProperties = { padding: `0 ${config.space.S200} 0 ${config.space.S400}`, @@ -51,7 +52,7 @@ function VerificationUnexpected({ message, onClose }: VerificationUnexpectedProp {message} ); @@ -60,8 +61,8 @@ function VerificationUnexpected({ message, onClose }: VerificationUnexpectedProp function VerificationWaitAccept() { return ( - Please accept the request from other device. - + {t('Settings.device_verification.please_accept_the_request_from_other_device')} + ); } @@ -75,7 +76,7 @@ function VerificationAccept({ onAccept }: VerificationAcceptProps) { const accepting = acceptState.status === AsyncStatus.Loading; return ( - Click accept to start the verification process. + {t('Settings.device_verification.click_accept_to_start_the_verification_process')} ); @@ -92,8 +93,8 @@ function VerificationAccept({ onAccept }: VerificationAcceptProps) { function VerificationWaitStart() { return ( - Verification request has been accepted. - + {t('Settings.device_verification.verification_request_has_been_accepted')} + ); } @@ -108,7 +109,7 @@ function AutoVerificationStart({ onStart }: VerificationStartProps) { return ( - + ); } @@ -130,7 +131,7 @@ function CompareEmoji({ sasData }: { sasData: ShowSasCallbacks }) { return ( - Confirm the emoji below are displayed on both devices, in the same order: + {t('Settings.device_verification.confirm_the_emoji_below_are_displayed_on_both_devices_in_the_same_order')} } > - They Match + {t('Settings.device_verification.they_match')} @@ -191,7 +192,7 @@ function SasVerification({ verifier, onCancel }: SasVerificationProps) { return ( - + ); } @@ -203,10 +204,10 @@ function VerificationDone({ onExit }: VerificationDoneProps) { return (
- Your device is verified. + {t('Settings.device_verification.your_device_is_verified')}
); @@ -218,9 +219,9 @@ type VerificationCanceledProps = { function VerificationCanceled({ onClose }: VerificationCanceledProps) { return ( - Verification has been canceled. + {t('Settings.device_verification.verification_has_been_canceled')} ); @@ -270,7 +271,7 @@ export function DeviceVerification({ request, onExit }: DeviceVerificationProps)
- Device Verification + {t('Settings.device_verification.device_verification')} @@ -294,7 +295,7 @@ export function DeviceVerification({ request, onExit }: DeviceVerificationProps) ) : ( ))} diff --git a/src/app/components/DeviceVerificationSetup.tsx b/src/app/components/DeviceVerificationSetup.tsx index 19902b4f05..f67086976b 100644 --- a/src/app/components/DeviceVerificationSetup.tsx +++ b/src/app/components/DeviceVerificationSetup.tsx @@ -83,7 +83,7 @@ function SetupVerification({ onComplete }: Readonly) { const handleAction = useCallback( async (authDict: AuthDict) => { if (!uiaAction) { - throw new Error(t('Settings.device_verification_setup.error_uia_action_without_data')); + throw new Error(t('Settings.device_verification.error_uia_action_without_data')); } if (alive()) { setNextAuthData(null); @@ -126,7 +126,7 @@ function SetupVerification({ onComplete }: Readonly) { if (alive()) { setUIAAction(action); } else { - reject(new Error(t('Settings.device_verification_setup.authentication_failed_failed_to_setup_device_verification'))); + reject(new Error(t('Settings.device_verification.authentication_failed_failed_to_setup_device_verification'))); } return; } @@ -140,11 +140,11 @@ function SetupVerification({ onComplete }: Readonly) { useCallback( async (passphrase) => { const crypto = mx.getCrypto(); - if (!crypto) throw new Error(t('Settings.device_verification_setup.unexpected_error_crypto_module_not_found')); + if (!crypto) throw new Error(t('Settings.device_verification.unexpected_error_crypto_module_not_found')); const recoveryKeyData = await crypto.createRecoveryKeyFromPassphrase(passphrase); if (!recoveryKeyData.encodedPrivateKey) { - throw new Error(t('Settings.device_verification_setup.unexpected_error_failed_to_create_recovery_key')); + throw new Error(t('Settings.device_verification.unexpected_error_failed_to_create_recovery_key')); } clearSecretStorageKeys(); @@ -185,7 +185,7 @@ function SetupVerification({ onComplete }: Readonly) { return ( - {t('Settings.device_verification_setup.generate_a')} {t('General.recovery_key')} {t('Settings.device_verification_setup.for_verifying_identity_if_you_do_not_have_access_to_other_devices_additiona')} + {t('Settings.device_verification.generate_a')} {t('General.recovery_key')} {t('Settings.device_verification.for_verifying_identity_if_you_do_not_have_access_to_other_devices_additiona')} {t('General.passphrase')} ({t('General.optional')}) @@ -208,7 +208,7 @@ function SetupVerification({ onComplete }: Readonly) { authData={nextAuthData ?? uiaAction.authData} unsupported={() => ( - {t('Settings.device_verification_setup.authentication_steps_to_perform_this_action_are_not_supported_by_client')} + {t('Settings.device_verification.authentication_steps_to_perform_this_action_are_not_supported_by_client')} )} > @@ -248,7 +248,7 @@ function RecoveryKeyDisplay({ recoveryKey }: Readonly) return ( - {t('Settings.device_verification_setup.store_the_recovery_key_in_a_safe_place_for_future_use_as_you_will_need_it_t')} + {t('Settings.device_verification.store_the_recovery_key_in_a_safe_place_for_future_use_as_you_will_need_it_t')} {t('General.recovery_key')} @@ -300,7 +300,7 @@ export const DeviceVerificationSetup = forwardRef - {t('Settings.device_verification_setup.setup_device_verification')} + {t('Settings.device_verification.setup_device_verification')} @@ -335,7 +335,7 @@ export const DeviceVerificationReset = forwardRef - {t('Settings.device_verification_setup.reset_device_verification')} + {t('Settings.device_verification.reset_device_verification')} @@ -357,10 +357,10 @@ export const DeviceVerificationReset = forwardRef βœ‹πŸ§‘β€πŸš’πŸ€š - {t('Settings.device_verification_setup.resetting_device_verification_is_permanent')} + {t('Settings.device_verification.resetting_device_verification_is_permanent')} - {t('Settings.device_verification_setup.anyone_you_have_verified_with_will_see_security_alerts_and_your_encryption')}{' '} - {t('General.recovery_key')} or {t('General.recovery_passphrase')} {t('Settings.device_verification_setup.and_every_device_you_can_verify_from')} + {t('Settings.device_verification.anyone_you_have_verified_with_will_see_security_alerts_and_your_encryption')}{' '} + {t('General.recovery_key')} or {t('General.recovery_passphrase')} {t('Settings.device_verification.and_every_device_you_can_verify_from')} ) : ( )} diff --git a/src/app/features/room/RoomViewTyping.tsx b/src/app/features/room/RoomViewTyping.tsx index dc01e4b7a1..cfd1dd3190 100644 --- a/src/app/features/room/RoomViewTyping.tsx +++ b/src/app/features/room/RoomViewTyping.tsx @@ -70,11 +70,11 @@ export const RoomViewTyping = as<'div', RoomViewTypingProps>( <> {typingNames[0]} - {t('RoomView.typing_sep_word')} + {t('RoomView.typing.typing_sep_word')} {typingNames[1]} - {t('RoomView.are_typing')} + {t('RoomView.typing.are_typing')} )} diff --git a/src/app/features/room/ThreadBrowser.tsx b/src/app/features/room/ThreadBrowser.tsx index 766889032f..eb3eeaa521 100644 --- a/src/app/features/room/ThreadBrowser.tsx +++ b/src/app/features/room/ThreadBrowser.tsx @@ -55,6 +55,7 @@ import { EncryptedContent } from './message'; import * as css from './ThreadDrawer.css'; import { SidebarResizer } from '$pages/client/sidebar/SidebarResizer'; import { mobileOrTablet } from '$utils/user-agent'; +import { t } from 'i18next'; type ThreadPreviewProps = { room: Room; @@ -215,7 +216,7 @@ function ThreadPreview({ room, thread, onClick, onJump }: ThreadPreviewProps) { )} - Jump + {t('RoomView.Threads.jump')} @@ -471,7 +472,7 @@ export function ThreadBrowser({ room, onOpenThread, onClose, overlay }: ThreadBr - Threads + {t('RoomView.Threads.threads')} @@ -480,7 +481,7 @@ export function ThreadBrowser({ room, onOpenThread, onClose, overlay }: ThreadBr variant="SurfaceVariant" size="300" radii="300" - aria-label="Close threads" + aria-label={t('RoomView.close_threads')} > @@ -497,7 +498,7 @@ export function ThreadBrowser({ room, onOpenThread, onClose, overlay }: ThreadBr ref={searchRef} value={query} onChange={handleSearchChange} - placeholder="Search threads..." + placeholder={t('RoomView.search_threads')} variant="Surface" size="400" radii="400" @@ -512,7 +513,7 @@ export function ThreadBrowser({ room, onOpenThread, onClose, overlay }: ThreadBr setQuery(''); searchRef.current?.focus(); }} - aria-label="Clear search" + aria-label={t('RoomView.clear_search')} > @@ -552,7 +553,7 @@ export function ThreadBrowser({ room, onOpenThread, onClose, overlay }: ThreadBr > - {lowerQuery ? 'No threads match your search.' : 'No threads yet.'} + {lowerQuery ? t('RoomView.Threads.no_threads_match_your_search') : t('RoomView.Threads.no_threads_yet')} ); diff --git a/src/app/features/room/ThreadDrawer.tsx b/src/app/features/room/ThreadDrawer.tsx index 070cdaf585..1e4907a10f 100644 --- a/src/app/features/room/ThreadDrawer.tsx +++ b/src/app/features/room/ThreadDrawer.tsx @@ -66,6 +66,7 @@ import { RoomViewFollowing, RoomViewFollowingPlaceholder } from './RoomViewFollo import * as css from './ThreadDrawer.css'; import { SidebarResizer } from '$pages/client/sidebar/SidebarResizer'; import { mobileOrTablet } from '$utils/user-agent'; +import { t } from 'i18next'; /** * Resolve the list of reply events to show in the thread drawer. @@ -777,7 +778,7 @@ export function ThreadDrawer({ room, threadRootId, onClose, overlay }: ThreadDra - Thread + {t('RoomView.Threads.thread')} @@ -870,7 +871,7 @@ export function ThreadDrawer({ room, threadRootId, onClose, overlay }: ThreadDra > - No replies yet. Start the thread below! + {t('RoomView.Threads.no_replies_yet_start_the_thread_below')} ); diff --git a/src/app/features/room/message/Message.tsx b/src/app/features/room/message/Message.tsx index ca1517de89..c808ad894b 100644 --- a/src/app/features/room/message/Message.tsx +++ b/src/app/features/room/message/Message.tsx @@ -83,6 +83,7 @@ import type { PerMessageProfileBeeperFormat } from '$hooks/usePerMessageProfile' import { convertBeeperFormatToOurPerMessageProfile } from '$hooks/usePerMessageProfile'; import { MessageEditor } from './MessageEditor'; import * as css from './styles.css'; +import { t } from 'i18next'; export type ReactionHandler = (keyOrMxc: string, shortcode: string) => void; @@ -193,7 +194,7 @@ export const MessagePinItem = as< ref={ref} > - {isPinned ? 'Unpin Message' : 'Pin Message'} + {isPinned ? t('RoomView.Message.unpin_message') : t('RoomView.Message.pin_message')} ); @@ -640,10 +641,10 @@ function MessageInternal( const originalRoomId = messageForwardedProps.originalRoomId; return { label: messageForwardedProps.originalEventPrivate - ? 'Forwarded private message' + ? t('RoomView.Message.forwarded_private_message') : isSameRoomForward(originalRoomId) - ? 'Forwarded from earlier in this room' - : 'Forwarded from another room', + ? t('RoomView.Message.forwarded_from_earlier_in_this_room') + : t('RoomView.Message.forwarded_from_another_room'), roomId: originalRoomId, eventId: messageForwardedProps.originalEventId, ts: messageForwardedProps.originalTimestamp ?? 0, @@ -655,8 +656,8 @@ function MessageInternal( const originalRoomId = msc2723ForwardedMessageProps.room_id; return { label: isSameRoomForward(originalRoomId) - ? 'Forwarded from earlier in this room' - : 'Forwarded from another room', + ? t('RoomView.Message.forwarded_from_earlier_in_this_room') + : t('RoomView.Message.forwarded_from_another_room'), roomId: originalRoomId, eventId: msc2723ForwardedMessageProps.event_id, ts: msc2723ForwardedMessageProps.origin_server_ts ?? 0, @@ -712,7 +713,7 @@ function MessageInternal( data-mention-event-id={forwardedNotice.eventId} onClick={mentionClickHandler} > - jump to original + {t('RoomView.Message.jump_to_original')} )} @@ -746,11 +747,11 @@ function MessageInternal( {isFailedSend && ( - Failed to send. + {t('RoomView.Message.failed_to_send')} {canResend && ( - Retry + {t('General.retry')} )} {canDeleteFailedSend && ( @@ -760,7 +761,7 @@ function MessageInternal( radii="Pill" onClick={handleDeleteFailedSendClick} > - Delete + {t('General.delete')} )} @@ -769,7 +770,7 @@ function MessageInternal( - Only you can see this. + {t('RoomView.Message.only_you_can_see_this')} - Dismiss + {t('General.dismiss')} )} @@ -1004,7 +1005,7 @@ function MessageInternal( size="T300" truncate > - Add Reaction + {t('RoomView.Message.add_reaction')} )} @@ -1033,7 +1034,7 @@ function MessageInternal( size="T300" truncate > - Add to User Sticker Pack + {t('RoomView.Message.add_to_user_sticker_pack')} )} @@ -1051,7 +1052,7 @@ function MessageInternal( }} > - Reply + {t('RoomView.Message.reply')} {!isThreadedMessage && ( @@ -1076,7 +1077,7 @@ function MessageInternal( size="T300" truncate > - Reply in Thread + {t('RoomView.Message.reply_in_thread')} )} @@ -1097,7 +1098,7 @@ function MessageInternal( size="T300" truncate > - Edit Message + {t('RoomView.Message.edit_message')} )} @@ -1128,7 +1129,7 @@ function MessageInternal( padding: `${config.space.S100} ${config.space.S200}`, }} > - Nickname + {t('General.nickname')} - Save + {t('General.save')} {nicknames[senderId] && ( - Clear + {t('General.clear')} )} @@ -1197,7 +1198,7 @@ function MessageInternal( size="T300" truncate > - {nicknames[senderId] ? 'Edit Nickname' : 'Set Nickname'} + {nicknames[senderId] ? t('General.edit_nickname') : t('General.set_nickname')} ))} @@ -1436,7 +1437,7 @@ export const Event = as<'div', EventProps>( size="T300" truncate > - Reply + {t('RoomView.Message.reply')} {!hideReadReceipts && ( diff --git a/src/app/features/room/message/MessageEditor.tsx b/src/app/features/room/message/MessageEditor.tsx index 10dbca6d7d..b14035a0cc 100644 --- a/src/app/features/room/message/MessageEditor.tsx +++ b/src/app/features/room/message/MessageEditor.tsx @@ -67,6 +67,8 @@ import { readdAngleBracketsForHiddenPreviews, stripMarkdownEscapesForHiddenPreviews, } from './hiddenLinkPreviews'; +import { t } from 'i18next'; +import * as prefixes from '$unstable/prefixes' type MessageEditorProps = { roomId: string; @@ -229,8 +231,8 @@ export const MessageEditor = as<'div', MessageEditorProps>( : undefined; const rawPmp = - editedEvent?.getContent()?.['m.new_content']?.['com.beeper.per_message_profile'] ?? - mEvent.getContent()?.['com.beeper.per_message_profile']; + editedEvent?.getContent()?.['m.new_content']?.[prefixes.MATRIX_UNSTABLE_PER_MESSAGE_PROFILE_PROPERTY_NAME] ?? + mEvent.getContent()?.[prefixes.MATRIX_UNSTABLE_PER_MESSAGE_PROFILE_PROPERTY_NAME]; const pmpDisplayname = rawPmp !== null && @@ -253,7 +255,7 @@ export const MessageEditor = as<'div', MessageEditorProps>( customHtml = htmlPrefix + customHtml; } - newContent['com.beeper.per_message_profile'] = rawPmp; + newContent[prefixes.MATRIX_UNSTABLE_PER_MESSAGE_PROFILE_PROPERTY_NAME] = rawPmp; } const contentBody: IContent & Omit, 'm.relates_to'> = { @@ -301,11 +303,11 @@ export const MessageEditor = as<'div', MessageEditorProps>( if (oldContent.file !== undefined) content['m.new_content'].file = oldContent.file; if (oldContent.url !== undefined) content['m.new_content'].url = oldContent.url; - if (oldContent['page.codeberg.everypizza.msc4193.spoiler'] !== undefined) { - content['page.codeberg.everypizza.msc4193.spoiler'] = - oldContent['page.codeberg.everypizza.msc4193.spoiler']; - content['m.new_content']['page.codeberg.everypizza.msc4193.spoiler'] = + if (oldContent[prefixes.MATRIX_UNSTABLE_SPOILER_PROPERTY_NAME] !== undefined) { + content[prefixes.MATRIX_UNSTABLE_SPOILER_PROPERTY_NAME] = oldContent['page.codeberg.everypizza.msc4193.spoiler']; + content['m.new_content'][prefixes.MATRIX_UNSTABLE_SPOILER_PROPERTY_NAME] = + oldContent[prefixes.MATRIX_UNSTABLE_SPOILER_PROPERTY_NAME]; } } content['com.beeper.linkpreviews'] = []; @@ -519,7 +521,7 @@ export const MessageEditor = as<'div', MessageEditorProps>( > ( ) : undefined } > - Save + {t('General.save')} - Cancel + {t('General.cancel')} diff --git a/src/app/features/room/message/Reactions.tsx b/src/app/features/room/message/Reactions.tsx index e2fc884782..038c28be3d 100644 --- a/src/app/features/room/message/Reactions.tsx +++ b/src/app/features/room/message/Reactions.tsx @@ -24,6 +24,7 @@ import { stopPropagation } from '$utils/keyboard'; import { useMediaAuthentication } from '$hooks/useMediaAuthentication'; import { ReactionViewer } from '$features/room/reaction-viewer'; import * as css from './styles.css'; +import { t } from 'i18next'; export type ReactionsProps = { room: Room; diff --git a/src/app/features/room/reaction-viewer/ReactionViewer.tsx b/src/app/features/room/reaction-viewer/ReactionViewer.tsx index 2f27fe5de3..95500de698 100644 --- a/src/app/features/room/reaction-viewer/ReactionViewer.tsx +++ b/src/app/features/room/reaction-viewer/ReactionViewer.tsx @@ -29,6 +29,7 @@ import { useOpenUserRoomProfile } from '$state/hooks/userRoomProfile'; import { useSpaceOptionally } from '$hooks/useSpace'; import { getMouseEventCords } from '$utils/dom'; import * as css from './ReactionViewer.css'; +import { t } from 'i18next'; export type ReactionViewerProps = { room: Room; @@ -99,7 +100,7 @@ export const ReactionViewer = as<'div', ReactionViewerProps>(
- {`Reacted with :${selectedShortcode}:`} + {t('RoomView.Reactions.reacted_with')} {`:${selectedShortcode}:`} From c029fe1c7a9aa9ad7aae59eb6c0052f806ab4b4a Mon Sep 17 00:00:00 2001 From: Rye Date: Sat, 23 May 2026 02:19:36 +0200 Subject: [PATCH 06/23] formatting :p --- .vscode/extensions.json | 6 +-- .vscode/settings.json | 13 ++---- src/app/components/DeviceVerification.tsx | 30 ++++++++++--- .../components/DeviceVerificationSetup.tsx | 45 ++++++++++++++----- src/app/features/room/RoomTombstone.tsx | 4 +- src/app/features/room/ThreadBrowser.tsx | 4 +- src/app/features/room/message/Message.tsx | 4 +- .../features/room/message/MessageEditor.tsx | 7 +-- .../room/reaction-viewer/ReactionViewer.tsx | 4 +- 9 files changed, 78 insertions(+), 39 deletions(-) diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 34b9eba949..f3e7809fb7 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,7 +1,3 @@ { - "recommendations": [ - "webpro.vscode-knip", - "oxc.oxc-vscode", - "lokalise.i18n-ally" - ] + "recommendations": ["webpro.vscode-knip", "oxc.oxc-vscode", "lokalise.i18n-ally"] } diff --git a/.vscode/settings.json b/.vscode/settings.json index 82961cddb6..e5946b5fa7 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -15,21 +15,14 @@ }, "nixEnvSelector.nixFile": "${workspaceFolder}/flake.nix", "nixEnvSelector.useFlakes": true, - "i18n-ally.localesPaths": [ - "public/locales" - ], + "i18n-ally.localesPaths": ["public/locales"], "i18n-ally.dirStructure": "auto", - "i18n-ally.enabledFrameworks": [ - "react", - "react-i18next" - ], + "i18n-ally.enabledFrameworks": ["react", "react-i18next"], "i18n-ally.extract.keyMaxLength": 75, "i18n-ally.extract.targetPickingStrategy": "most-similar", "i18n-ally.keystyle": "nested", "i18n-ally.extract.keygenStyle": "snake_case", "i18n-ally.extract.ignoredByFiles": { - "src/app/components/DeviceVerificationSetup.tsx": [ - "Column" - ] + "src/app/components/DeviceVerificationSetup.tsx": ["Column"] } } diff --git a/src/app/components/DeviceVerification.tsx b/src/app/components/DeviceVerification.tsx index af7e256d78..25e51d9f35 100644 --- a/src/app/components/DeviceVerification.tsx +++ b/src/app/components/DeviceVerification.tsx @@ -62,7 +62,9 @@ function VerificationWaitAccept() { return ( {t('Settings.device_verification.please_accept_the_request_from_other_device')} - + ); } @@ -76,7 +78,9 @@ function VerificationAccept({ onAccept }: VerificationAcceptProps) { const accepting = acceptState.status === AsyncStatus.Loading; return ( - {t('Settings.device_verification.click_accept_to_start_the_verification_process')} + + {t('Settings.device_verification.click_accept_to_start_the_verification_process')} + diff --git a/src/app/features/settings/Persona/PerMessageProfileEditor.tsx b/src/app/features/settings/Persona/PerMessageProfileEditor.tsx index f05129eae2..5ea59647bc 100644 --- a/src/app/features/settings/Persona/PerMessageProfileEditor.tsx +++ b/src/app/features/settings/Persona/PerMessageProfileEditor.tsx @@ -17,6 +17,7 @@ import { import type { PronounSet } from '$utils/pronouns'; import { parsePronounsStringToPronounsSetArray } from '$utils/pronouns'; import { SequenceCardStyle } from '../styles.css'; +import { t } from 'i18next'; /** * the props we use for the per-message profile editor, which is used to edit a per-message profile. This is used in the settings page when the user wants to edit a profile. @@ -226,7 +227,7 @@ export function PerMessageProfileEditor({ style={{ width: '100%', marginBottom: config.space.S200 }} > - Profile ID: + {t('Settings.PerMessageProfiles.profile_id')} @@ -263,7 +264,7 @@ export function PerMessageProfileEditor({ overflow: 'visible', marginTop: 20, }} - aria-label="Avatar and upload" + aria-label={t('Settings.Profile.avatar_and_upload')} > ( - + p )} @@ -303,9 +304,9 @@ export function PerMessageProfileEditor({ fontSize: 14, padding: '0 8px', }} - aria-label="Upload avatar image" + aria-label={t('Settings.Profile.upload_avatar_image')} > - Upload + {t('General.upload')} {uploadAtom && ( - Display Name: + {t('Settings.PerMessageProfiles.display_name')} @@ -408,7 +409,7 @@ export function PerMessageProfileEditor({ fontSize: 16, height: 50, }} - placeholder="Pronouns" + placeholder={t('General.pronouns')} readOnly={changingDisplayName || disableSetDisplayname} aria-label={`Pronouns for ${profileId}`} title={`Pronouns for ${profileId}`} @@ -477,7 +478,7 @@ export function PerMessageProfileEditor({ aria-label={`Delete profile ${profileId}`} title={`Delete profile ${profileId}`} > - Delete + {t('General.delete')} diff --git a/src/app/features/settings/cosmetics/Cosmetics.tsx b/src/app/features/settings/cosmetics/Cosmetics.tsx index 49ab59374d..7f6e917b41 100644 --- a/src/app/features/settings/cosmetics/Cosmetics.tsx +++ b/src/app/features/settings/cosmetics/Cosmetics.tsx @@ -26,14 +26,15 @@ import { SequenceCardStyle } from '$features/settings/styles.css'; import { SettingsSectionPage } from '../SettingsSectionPage'; import { Appearance } from './Themes'; import { LanguageSpecificPronouns } from './LanguageSpecificPronouns'; +import { t } from 'i18next'; const emojiSizeItems = [ - { id: 'none', name: 'None (Same size as text)' }, - { id: 'extraSmall', name: 'Extra Small' }, - { id: 'small', name: 'Small' }, - { id: 'normal', name: 'Normal' }, - { id: 'large', name: 'Large' }, - { id: 'extraLarge', name: 'Extra Large' }, + { id: 'none', name: t('Settings.Cosmetics.none_same_size_as_text') }, + { id: 'extraSmall', name: t('Settings.Cosmetics.extra_small') }, + { id: 'small', name: t('Settings.Cosmetics.small') }, + { id: 'normal', name: t('Settings.Cosmetics.normal') }, + { id: 'large', name: t('Settings.Cosmetics.large') }, + { id: 'extraLarge', name: t('Settings.Cosmetics.extra_large') }, ]; function SelectJumboEmojiSize() { @@ -105,10 +106,10 @@ function SelectJumboEmojiSize() { } const profileCardRenderItems: { id: RenderUserCardsMode; name: string }[] = [ - { id: 'both', name: 'Light & dark' }, - { id: 'light', name: 'Light only' }, - { id: 'dark', name: 'Dark only' }, - { id: 'none', name: 'Off' }, + { id: 'both', name: t('Settings.Cosmetics.light_and_dark') }, + { id: 'light', name: t('Settings.Cosmetics.light_only') }, + { id: 'dark', name: t('Settings.Cosmetics.dark_only') }, + { id: 'none', name: t('Settings.Cosmetics.off') }, ]; function SelectRenderCustomProfileCards() { @@ -125,7 +126,7 @@ function SelectRenderCustomProfileCards() { }; const currentLabel = - profileCardRenderItems.find((i) => i.id === renderUserCardsMode)?.name ?? 'Light & dark'; + profileCardRenderItems.find((i) => i.id === renderUserCardsMode)?.name ?? t('Settings.Cosmetics.light_and_dark'); return ( <> @@ -183,12 +184,12 @@ function SelectRenderCustomProfileCards() { function JumboEmoji() { return ( - Jumbo Emoji + {t('Settings.Cosmetics.jumbo_emoji')} } /> @@ -206,22 +207,22 @@ function Privacy() { return ( - Privacy & Security + {t('Settings.Cosmetics.privacy_and_security')} } /> } @@ -230,9 +231,9 @@ function Privacy() { } @@ -259,12 +260,12 @@ function IdentityCosmetics() { return ( - Identity + {t('Settings.Cosmetics.identity')} } /> } /> } /> } @@ -310,9 +311,9 @@ function IdentityCosmetics() { } @@ -320,17 +321,17 @@ function IdentityCosmetics() { } /> } /> diff --git a/src/app/features/settings/cosmetics/LanguageSpecificPronouns.tsx b/src/app/features/settings/cosmetics/LanguageSpecificPronouns.tsx index dbab076a8b..b68f6a9011 100644 --- a/src/app/features/settings/cosmetics/LanguageSpecificPronouns.tsx +++ b/src/app/features/settings/cosmetics/LanguageSpecificPronouns.tsx @@ -4,6 +4,7 @@ import { SequenceCard } from '$components/sequence-card'; import { useEffect, useState } from 'react'; import { getSettings, setSettings } from '$state/settings'; import { SequenceCardStyle } from '../styles.css'; +import { t } from 'i18next'; export type LanguageSpecificPronounsConfig = { enabled?: boolean | string; @@ -69,7 +70,7 @@ export function LanguageSpecificPronouns() { return ( - Language Specific Pronouns + {t('Settings.Cosmetics.language_specific_pronouns')} {useLanguageSpecificPronouns && ( Date: Sat, 30 May 2026 00:15:21 +0200 Subject: [PATCH 18/23] implement localization for EmojiBoard component and related labels --- public/locales/en.json | 11 +++++++++-- src/app/components/emoji-board/EmojiBoard.tsx | 13 +++++++------ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/public/locales/en.json b/public/locales/en.json index c645b797ae..1a8a0483a6 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -119,7 +119,8 @@ "upload": "Upload", "upload_area": "Upload area", "display_name": "Display name", - "pronouns": "Pronouns" + "pronouns": "Pronouns", + "unknown": "Unknown" }, "RoomView": { "failed_to_load_history": "Failed to load history.", @@ -174,7 +175,13 @@ } }, "RoomInput": { - "recording_duration": "Recording duration:" + "recording_duration": "Recording duration:", + "EmojiBoard": { + "no_results_found": "No Results found", + "search_results": "Search Results", + "personal_pack": "Personal Pack", + "unknown_pack": "Unknown Pack" + } }, "RoomCreate": { "versions": "Versions", diff --git a/src/app/components/emoji-board/EmojiBoard.tsx b/src/app/components/emoji-board/EmojiBoard.tsx index d209b2d0a4..02c831fc2f 100644 --- a/src/app/components/emoji-board/EmojiBoard.tsx +++ b/src/app/components/emoji-board/EmojiBoard.tsx @@ -55,6 +55,7 @@ import { EmojiBoardLayout, } from './components'; import { EmojiBoardTab, EmojiType } from './types'; +import { t } from 'i18next'; const RECENT_GROUP_ID = 'recent_group'; const SEARCH_GROUP_ID = 'search_group'; @@ -91,7 +92,7 @@ const useGroups = ( imagePacks.forEach((pack) => { let label = pack.meta.name; - if (!label) label = isUserId(pack.id) ? 'Personal Pack' : mx.getRoom(pack.id)?.name; + if (!label) label = isUserId(pack.id) ? t('RoomInput.EmojiBoard.personal_pack') : mx.getRoom(pack.id)?.name; g.push({ id: pack.id, @@ -119,7 +120,7 @@ const useGroups = ( imagePacks.forEach((pack) => { let label = pack.meta.name; - if (!label) label = isUserId(pack.id) ? 'Personal Pack' : mx.getRoom(pack.id)?.name; + if (!label) label = isUserId(pack.id) ? t('RoomInput.EmojiBoard.personal_pack') : mx.getRoom(pack.id)?.name; g.push({ id: pack.id, @@ -210,7 +211,7 @@ function EmojiSidebar({ {packs.map((pack) => { let label = pack.meta.name; - if (!label) label = isUserId(pack.id) ? 'Personal Pack' : mx.getRoom(pack.id)?.name; + if (!label) label = isUserId(pack.id) ? t('RoomInput.EmojiBoard.personal_pack') : mx.getRoom(pack.id)?.name; // limit width and height to 36 to prevent very large icons from breaking the layout, since custom emoji pack icons can be of any size // trying to get close to the render target size of the icons in the sidebar, which is around 24px @@ -223,7 +224,7 @@ function EmojiSidebar({ key={pack.id} active={activeGroupId === pack.id} id={pack.id} - label={label ?? 'Unknown Pack'} + label={label ?? t('RoomInput.EmojiBoard.unknown_pack')} url={url ?? undefined} onClick={handleScrollToGroup} /> @@ -295,7 +296,7 @@ function StickerSidebar({ key={pack.id} active={activeGroupId === pack.id} id={pack.id} - label={label ?? 'Unknown Pack'} + label={label ?? t('RoomInput.EmojiBoard.unknown_pack')} url={url ?? undefined} onClick={handleScrollToGroup} /> @@ -588,7 +589,7 @@ export function EmojiBoard({ {searchedItems && ( {searchedItems.map((element, index) => renderItem(element, index))} From c54c2bbe331fba0bd35e8edd8f3dc0edca815ed0 Mon Sep 17 00:00:00 2001 From: Rye Date: Sat, 30 May 2026 00:22:32 +0200 Subject: [PATCH 19/23] implement localization for settings and experimental features --- public/locales/en.json | 25 +++++++++++++++++-- src/app/components/AccountDataEditor.tsx | 17 +++++++------ .../experimental/BandwithSavingEmojis.tsx | 7 +++--- .../settings/experimental/Experimental.tsx | 15 +++++------ .../experimental/MSC4268HistoryShare.tsx | 11 ++++---- 5 files changed, 50 insertions(+), 25 deletions(-) diff --git a/public/locales/en.json b/public/locales/en.json index 1a8a0483a6..7ef9d04211 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -89,7 +89,22 @@ "selected_language_for_pronouns": "Selected language for pronouns", "the_language_to_show_pronouns_for_when_the_above_setting_is_enabled": "The language to show pronouns for when the above setting is enabled.", "language_code_e_g_en_de_en_de": "Language code (e.g. 'en', 'de', 'en,de')" - } + }, + "save_bandwidth_for_sticker_and_emoji_images": "Save Bandwidth for Sticker and Emoji Images", + "enable_bandwidth_saving_for_stickers_and_emojis": "Enable bandwidth saving for stickers and emojis", + "if_enabled_sticker_and_emoji_images_will_be_optimized_to_save_bandwidth_thi": "If enabled, sticker and emoji images will be optimized to save bandwidth. This helps reduce data usage when viewing these images. But will increase server computation load.", + "personas_per_message_profiles": "Personas (Per-Message Profiles)", + "show_personas_tab": "Show Personas Tab", + "enables_the_personas_tab_in_the_settings_menu_for_per_message_profiles": "Enables the personas tab in the settings menu for per-message profiles", + "experimental": "Experimental", + "the_features_listed_below_may_be_unstable_or_incomplete": "The features listed below may be unstable or incomplete,", + "use_at_your_own_risk": "use at your own risk", + "please_report_any_new_issues_potentially_caused_by_these_features": "Please report any new issues potentially caused by these features!", + "enable_sharing_of_encrypted_history": "Enable Sharing of Encrypted History", + "enable_the_sharehistory_command": "Enable the /sharehistory command", + "if_enabled_this_command_will_allow_users_to_share_encrypted_history_with_ot": "If enabled, this command will allow users to share encrypted history with other newly joined users, as per MSC4268.", + "disable_sharehistory_command": "Disable /sharehistory command", + "enable_sharehistory_command": "Enable /sharehistory command" }, "General": { "continue": "Continue", @@ -120,7 +135,8 @@ "upload_area": "Upload area", "display_name": "Display name", "pronouns": "Pronouns", - "unknown": "Unknown" + "unknown": "Unknown", + "edit": "Edit" }, "RoomView": { "failed_to_load_history": "Failed to load history.", @@ -214,5 +230,10 @@ "invite_to_direct_message_anyway": "Invite to Direct Message anyway", "invite_another_member": "Invite another Member" } + }, + "DevTools": { + "json_content": "JSON Content", + "account_data": "Account Data", + "developer_tools": "Developer Tools" } } diff --git a/src/app/components/AccountDataEditor.tsx b/src/app/components/AccountDataEditor.tsx index 131ae7fab3..953ccbfd67 100644 --- a/src/app/components/AccountDataEditor.tsx +++ b/src/app/components/AccountDataEditor.tsx @@ -24,6 +24,7 @@ import { useTextAreaCodeEditor } from '$hooks/useTextAreaCodeEditor'; import { Page, PageHeader } from './page'; import { SequenceCard } from './sequence-card'; import { TextViewerContent } from './text-viewer'; +import { t } from 'i18next'; const EDITOR_INTENT_SPACE_COUNT = 2; @@ -122,7 +123,7 @@ function AccountDataEdit({ aria-disabled={submitting} > - Account Data + {t('DevTools.account_data')} } > - Save + {t('General.save')} @@ -166,7 +167,7 @@ function AccountDataEdit({ - JSON Content + {t('DevTools.json_content')} - Account Data + {t('DevTools.account_data')} - JSON Content + {t('DevTools.json_content')} } > - Developer Tools + {t('DevTools.developer_tools')} diff --git a/src/app/features/settings/experimental/BandwithSavingEmojis.tsx b/src/app/features/settings/experimental/BandwithSavingEmojis.tsx index 6240f2369d..89f0a28f29 100644 --- a/src/app/features/settings/experimental/BandwithSavingEmojis.tsx +++ b/src/app/features/settings/experimental/BandwithSavingEmojis.tsx @@ -4,6 +4,7 @@ import { useSetting } from '$state/hooks/settings'; import { settingsAtom } from '$state/settings'; import { Box, Switch, Text } from 'folds'; import { SequenceCardStyle } from '../styles.css'; +import { t } from 'i18next'; export function BandwidthSavingEmojis() { const [useBandwidthSaving, setUseBandwidthSaving] = useSetting( @@ -13,7 +14,7 @@ export function BandwidthSavingEmojis() { return ( - Save Bandwidth for Sticker and Emoji Images + {t('Settings.save_bandwidth_for_sticker_and_emoji_images')} } diff --git a/src/app/features/settings/experimental/Experimental.tsx b/src/app/features/settings/experimental/Experimental.tsx index 3304121855..c12456aad8 100644 --- a/src/app/features/settings/experimental/Experimental.tsx +++ b/src/app/features/settings/experimental/Experimental.tsx @@ -10,6 +10,7 @@ import { Sync } from '../general'; import { SettingsSectionPage } from '../SettingsSectionPage'; import { BandwidthSavingEmojis } from './BandwithSavingEmojis'; import { MSC4268HistoryShare } from './MSC4268HistoryShare'; +import { t } from 'i18next'; function PersonaToggle() { const [showPersonaSetting, setShowPersonaSetting] = useSetting( @@ -19,12 +20,12 @@ function PersonaToggle() { return ( - Personas (Per-Message Profiles) + {t('Settings.personas_per_message_profiles')} } @@ -40,7 +41,7 @@ type ExperimentalProps = { }; export function Experimental({ requestBack, requestClose }: Readonly) { return ( - + @@ -49,10 +50,10 @@ export function Experimental({ requestBack, requestClose }: Readonly - The features listed below may be unstable or incomplete,{' '} - use at your own risk. + {t('Settings.the_features_listed_below_may_be_unstable_or_incomplete')}{' '} + {t('Settings.use_at_your_own_risk')}.
- Please report any new issues potentially caused by these features! + {t('Settings.please_report_any_new_issues_potentially_caused_by_these_features')} } /> diff --git a/src/app/features/settings/experimental/MSC4268HistoryShare.tsx b/src/app/features/settings/experimental/MSC4268HistoryShare.tsx index 27c868d19b..b130193d22 100644 --- a/src/app/features/settings/experimental/MSC4268HistoryShare.tsx +++ b/src/app/features/settings/experimental/MSC4268HistoryShare.tsx @@ -4,6 +4,7 @@ import { useSetting } from '$state/hooks/settings'; import { settingsAtom } from '$state/settings'; import { Box, Switch, Text } from 'folds'; import { SequenceCardStyle } from '../styles.css'; +import { t } from 'i18next'; export function MSC4268HistoryShare() { const [enabledMSC4268Command, setEnabledMSC4268Command] = useSetting( @@ -13,7 +14,7 @@ export function MSC4268HistoryShare() { return ( - Enable Sharing of Encrypted History + {t('Settings.enable_sharing_of_encrypted_history')} } From a5661771a73525137fd8bddae231bc1cff01c841 Mon Sep 17 00:00:00 2001 From: Rye Date: Sat, 30 May 2026 00:32:10 +0200 Subject: [PATCH 20/23] implement localization for General, KeyboardShortcuts, and Persona settings --- public/locales/en.json | 43 ++++++++++++++++++- .../features/settings/Persona/PKCompat.tsx | 17 ++++---- src/app/features/settings/general/General.tsx | 33 +++++++------- .../keyboard-shortcuts/KeyboardShortcuts.tsx | 25 +++++------ 4 files changed, 81 insertions(+), 37 deletions(-) diff --git a/public/locales/en.json b/public/locales/en.json index 7ef9d04211..ae754c5149 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -104,7 +104,48 @@ "enable_the_sharehistory_command": "Enable the /sharehistory command", "if_enabled_this_command_will_allow_users_to_share_encrypted_history_with_ot": "If enabled, this command will allow users to share encrypted history with other newly joined users, as per MSC4268.", "disable_sharehistory_command": "Disable /sharehistory command", - "enable_sharehistory_command": "Enable /sharehistory command" + "enable_sharehistory_command": "Enable /sharehistory command", + "General": { + "formatting": "Formatting", + "month": "Month", + "day_of_the_week": "Day of the Week", + "day_of_the_week_sunday_0": "Day of the week (Sunday = 0)", + "two_letter_day_name": "Two-letter day name", + "short_day_name": "Short day name", + "full_day_name": "Full day name", + "day_of_the_month": "Day of the Month", + "full_month_name": "Full month name", + "short_month_name": "Short month name", + "the_month": "The month", + "two_digit_month": "Two-digit month", + "four_digit_year": "Four-digit year", + "two_digit_year": "Two-digit year" + }, + "KeyboardShortcuts": { + "jump_to_the_highest_priority_unread_room": "Jump to the highest-priority unread room", + "go_to_next_unread_room_cycle": "Go to next unread room (cycle)", + "go_to_previous_unread_room_cycle": "Go to previous unread room (cycle)", + "undo_in_message_editor": "Undo in message editor", + "redo_in_message_editor": "Redo in message editor", + "bold": "Bold", + "italic": "Italic", + "underline": "Underline", + "keyboard_shortcuts": "Keyboard Shortcuts", + "close_keyboard_shortcuts": "Close keyboard shortcuts", + "messages": "Messages", + "navigation": "Navigation" + }, + "Persona": { + "limited_compatibility_with_pluralkit_like_functions": "Limited Compatibility with PluralKit-like functions", + "enable_pk_commands": "Enable PK commands", + "if_enabled_it_will_enable_a_few_pk_style_commands_currently_verry_limited": "If enabled, it will enable a few pk style commands, currently verry limited", + "disable_pk_commands": "disable pk; commands", + "enable_pks_commands": "enable pk; commands", + "enable_shorthands": "Enable Shorthands", + "if_enabled_you_can_use_shorthands_to_use_a_persona_for_one_message_only_eg": "If enabled, you can use shorthands to use a Persona for one message only (eg. '✨:test')", + "disable_checking_typed_messages_for_shorthands": "disable checking typed messages for shorthands", + "enable_checking_typed_messages_for_shorthands": "enable checking typed messages for shorthands" + } }, "General": { "continue": "Continue", diff --git a/src/app/features/settings/Persona/PKCompat.tsx b/src/app/features/settings/Persona/PKCompat.tsx index 8d55eaec1b..6d84561c70 100644 --- a/src/app/features/settings/Persona/PKCompat.tsx +++ b/src/app/features/settings/Persona/PKCompat.tsx @@ -4,6 +4,7 @@ import { useSetting } from '$state/hooks/settings'; import { settingsAtom } from '$state/settings'; import { Box, Switch, Text } from 'folds'; import { SequenceCardStyle } from '../styles.css'; +import { t } from 'i18next'; export function PKCompatSettings() { const [usePKCompat, setUsePKCompat] = useSetting(settingsAtom, 'pkCompat'); @@ -11,7 +12,7 @@ export function PKCompatSettings() { return ( - Limited Compatibility with PluralKit-like functions + {t('Settings.Persona.limited_compatibility_with_pluralkit_like_functions')} } /> } diff --git a/src/app/features/settings/general/General.tsx b/src/app/features/settings/general/General.tsx index 29361bdd69..49066da765 100644 --- a/src/app/features/settings/general/General.tsx +++ b/src/app/features/settings/general/General.tsx @@ -47,6 +47,7 @@ import { isKeyHotkey } from 'is-hotkey'; import { settingsSyncLastSyncedAtom, settingsSyncStatusAtom } from '$hooks/useSettingsSync'; import { exportSettingsAsJson, importSettingsFromJson } from '$utils/settingsSync'; import { SettingsSectionPage } from '../SettingsSectionPage'; +import { t } from 'i18next'; type DateHintProps = { hasChanges: boolean; @@ -75,26 +76,26 @@ function DateHint({ hasChanges, handleReset }: Readonly) { >
- Formatting + {t('Settings.General.formatting')}
- Year + {t('Settings.General.year')}
YY {': '} - Two-digit year + {t('Settings.General.two_digit_year')} {' '} YYYY - {': '}Four-digit year + {': '}{t('Settings.General.four_digit_year')} @@ -102,31 +103,31 @@ function DateHint({ hasChanges, handleReset }: Readonly) {
- Month + {t('Settings.General.month')}
M - {': '}The month + {': '}{t('Settings.General.the_month')} MM - {': '}Two-digit month + {': '}{t('Settings.General.two_digit_month')} {' '} MMM - {': '}Short month name + {': '}{t('Settings.General.short_month_name')} MMMM - {': '}Full month name + {': '}{t('Settings.General.full_month_name')} @@ -134,13 +135,13 @@ function DateHint({ hasChanges, handleReset }: Readonly) {
- Day of the Month + {t('Settings.General.day_of_the_month')}
D - {': '}Day of the month + {': '}{t('Settings.General.day_of_the_month')} @@ -153,31 +154,31 @@ function DateHint({ hasChanges, handleReset }: Readonly) {
- Day of the Week + {t('Settings.General.day_of_the_week')}
d - {': '}Day of the week (Sunday = 0) + {': '}{t('Settings.General.day_of_the_week_sunday_0')} dd - {': '}Two-letter day name + {': '}{t('Settings.General.two_letter_day_name')} ddd - {': '}Short day name + {': '}{t('Settings.General.short_day_name')} dddd - {': '}Full day name + {': '}{t('Settings.General.full_day_name')} diff --git a/src/app/features/settings/keyboard-shortcuts/KeyboardShortcuts.tsx b/src/app/features/settings/keyboard-shortcuts/KeyboardShortcuts.tsx index a0b52da48e..79105ef974 100644 --- a/src/app/features/settings/keyboard-shortcuts/KeyboardShortcuts.tsx +++ b/src/app/features/settings/keyboard-shortcuts/KeyboardShortcuts.tsx @@ -7,6 +7,7 @@ import { Box, Scroll, Text, config } from 'folds'; import { PageContent } from '$components/page'; import { SettingsSectionPage } from '../SettingsSectionPage'; +import { t } from 'i18next'; type ShortcutEntry = { keys: string; @@ -29,21 +30,21 @@ function formatKey(key: string): string { const SHORTCUT_CATEGORIES: ShortcutCategory[] = [ { - name: 'Navigation', + name: t('Settings.KeyboardShortcuts.navigation'), shortcuts: [ - { keys: 'Alt+N', description: 'Jump to the highest-priority unread room' }, - { keys: 'Alt+Shift+Down', description: 'Go to next unread room (cycle)' }, - { keys: 'Alt+Shift+Up', description: 'Go to previous unread room (cycle)' }, + { keys: 'Alt+N', description: t('Settings.KeyboardShortcuts.jump_to_the_highest_priority_unread_room') }, + { keys: 'Alt+Shift+Down', description: t('Settings.KeyboardShortcuts.go_to_next_unread_room_cycle') }, + { keys: 'Alt+Shift+Up', description: t('Settings.KeyboardShortcuts.go_to_previous_unread_room_cycle') }, ], }, { - name: 'Messages', + name: t('Settings.KeyboardShortcuts.messages'), shortcuts: [ - { keys: 'Ctrl+Z / ⌘+Z', description: 'Undo in message editor' }, - { keys: 'Ctrl+Shift+Z / ⌘+Shift+Z', description: 'Redo in message editor' }, - { keys: 'Ctrl+B / ⌘+B', description: 'Bold' }, - { keys: 'Ctrl+I / ⌘+I', description: 'Italic' }, - { keys: 'Ctrl+U / ⌘+U', description: 'Underline' }, + { keys: 'Ctrl+Z / ⌘+Z', description: t('Settings.KeyboardShortcuts.undo_in_message_editor') }, + { keys: 'Ctrl+Shift+Z / ⌘+Shift+Z', description: t('Settings.KeyboardShortcuts.redo_in_message_editor') }, + { keys: 'Ctrl+B / ⌘+B', description: t('Settings.KeyboardShortcuts.bold') }, + { keys: 'Ctrl+I / ⌘+I', description: t('Settings.KeyboardShortcuts.italic') }, + { keys: 'Ctrl+U / ⌘+U', description: t('Settings.KeyboardShortcuts.underline') }, ], }, ]; @@ -112,9 +113,9 @@ type KeyboardShortcutsProps = { export function KeyboardShortcuts({ requestBack, requestClose }: KeyboardShortcutsProps) { return ( From 136cfc4e252fb82779a7051c84e01594b451cf37 Mon Sep 17 00:00:00 2001 From: Shea Date: Thu, 16 Jul 2026 13:06:54 +0300 Subject: [PATCH 21/23] make full romanian file, and delete fr and de files as i do not speak either to make the files properly Signed-off-by: Shea --- i18next.config.ts | 7 + public/locales/de.json | 118 -------- public/locales/en.json | 18 +- public/locales/fr.json | 118 -------- public/locales/ro.json | 273 ++++++++++++++++++ .../components/DeviceVerificationSetup.tsx | 9 +- .../DirectInvitePrompt.tsx | 9 +- src/app/features/room/RoomCallButton.test.tsx | 5 +- .../features/settings/cosmetics/Cosmetics.tsx | 48 +-- src/app/features/settings/general/General.tsx | 48 +++ src/app/features/settings/settingsLink.ts | 1 + src/app/i18n.ts | 6 +- 12 files changed, 373 insertions(+), 287 deletions(-) create mode 100644 i18next.config.ts delete mode 100644 public/locales/de.json delete mode 100644 public/locales/fr.json create mode 100644 public/locales/ro.json diff --git a/i18next.config.ts b/i18next.config.ts new file mode 100644 index 0000000000..fc2fbae4f2 --- /dev/null +++ b/i18next.config.ts @@ -0,0 +1,7 @@ +export default { + locales: ['en', 'ro'], + extract: { + input: 'src/**/*.tsx', + output: 'public/locales/{{language}}/{{namespace}}.json', + }, +}; diff --git a/public/locales/de.json b/public/locales/de.json deleted file mode 100644 index 7e363da3ad..0000000000 --- a/public/locales/de.json +++ /dev/null @@ -1,118 +0,0 @@ -{ - "Organisms": { - "RoomCommon": { - "changed_room_name": " hat den Raum Name geΓ€ndert" - } - }, - "Settings": { - "device_verification": { - "error_uia_action_without_data": "Unerwarteter Fehler! UIA-Aktion wird ohne Daten ausgefΓΌhrt.", - "authentication_failed_failed_to_setup_device_verification": "Authentifizierung fehlgeschlagen! Fehler beim Einrichten der GerΓ€teverifizierung.", - "unexpected_error_crypto_module_not_found": "Unerwarteter Fehler! \nKryptomodul nicht gefunden!", - "unexpected_error_failed_to_create_recovery_key": "Unexpected Error! Failed to create recovery key.", - "generate_a": "Generate a", - "for_verifying_identity_if_you_do_not_have_access_to_other_devices_additiona": "for verifying identity if you do not have access to other devices. Additionally, setup a passphrase as a memorable alternative.", - "authentication_steps_to_perform_this_action_are_not_supported_by_client": "Authentication steps to perform this action are not supported by client.", - "store_the_recovery_key_in_a_safe_place_for_future_use_as_you_will_need_it_t": "Store the Recovery Key in a safe place for future use, as you will need it to verify your identity if you do not have access to other devices.", - "setup_device_verification": "Richten Sie die GerΓ€teverifizierung ein", - "reset_device_verification": "GerΓ€teverifizierung zurΓΌcksetzen", - "resetting_device_verification_is_permanent": "Resetting device verification is permanent.", - "anyone_you_have_verified_with_will_see_security_alerts_and_your_encryption": "Anyone you have verified with will see security alerts and your encryption backup will be lost. You almost certainly do not want to do this, unless you have lost", - "and_every_device_you_can_verify_from": "and every device you can verify from.", - "please_accept_the_request_from_other_device": "Please accept the request from other device.", - "click_accept_to_start_the_verification_process": "Click accept to start the verification process.", - "waiting_for_request_to_be_accepted": "Waiting for request to be accepted...", - "confirm_the_emoji_below_are_displayed_on_both_devices_in_the_same_order": "Confirm the emoji below are displayed on both devices, in the same order:", - "they_match": "Sie passen zusammen", - "do_not_match": "Do not Match", - "starting_verification_using_emoji_comparison": "Verifizierung mit Emoji-Vergleich wird gestartet...", - "your_device_is_verified": "Your device is verified.", - "verification_has_been_canceled": "Verification has been canceled.", - "device_verification": "Device Verification", - "unexpected_error_verification_is_started_but_verifier_is_missing": "Unerwarteter Fehler! \nDie Verifizierung wurde gestartet, aber der PrΓΌfer fehlt.", - "verification_request_has_been_accepted": "Verification request has been accepted.", - "waiting_for_the_response_from_other_device": "Waiting for the response from other device..." - } - }, - "General": { - "continue": "Weitermachen", - "passphrase": "Passphrase", - "optional": "optional", - "unexpected_error": "Unerwarteter Fehler!", - "recovery_key": "WiederherstellungsschlΓΌssel", - "download": "Herunterladen", - "copy": "kopieren", - "hide": "Verstecken", - "show": "Zeigen", - "recovery_passphrase": "Wiederherstellungspassphrase", - "reset": "ZurΓΌcksetzen", - "close": "Schließen", - "accept": "Akzeptieren", - "okay": "Verstanden", - "retry": "Wiederholen", - "user": "Benutzer", - "delete": "LΓΆschen", - "dismiss": "Verwerfen", - "nickname": "Spitzname", - "save": "Speichern", - "clear": "Leeren", - "edit_nickname": "Spitzname bearbeiten", - "set_nickname": "Spitznamen festlegen", - "cancel": "Abbrechen" - }, - "RoomView": { - "failed_to_load_history": "Failed to load history.", - "failed_to_load_messages": "Failed to load messages.", - "jump_to_unread": "Zu β€žUngelesenβ€œ wechseln", - "mark_as_read": "Als gelesen markieren", - "new_messages": "Neue Nachrichten", - "jump_to_latest": "Zum Neuesten springen", - "call_started_by": "Anruf gestartet von", - "start_voice_call": "Sprachanruf starten", - "typing": { - "are_typing": "schreiben...", - "typing_sep_word": " and " - }, - "is_typing": "tippt...", - "close_threads": "Themen schließen", - "search_threads": "Threads durchsuchen...", - "clear_search": "Suche lΓΆschen", - "Threads": { - "no_threads_match_your_search": "No threads match your search.", - "no_threads_yet": "No threads yet.", - "jump": "Springen", - "threads": "Themen", - "thread": "Thema", - "no_replies_yet_start_the_thread_below": "No replies yet. Start the thread below!" - }, - "join_new_room": "Neuem Raum beitreten", - "this_room_has_been_replaced_and_is_no_longer_active": "This room has been replaced and is no longer active.", - "failed_to_join_replacement_room": "Failed to join replacement room!", - "open_new_room": "Neuen Raum ΓΆffnen", - "scroll_to_top": "Nach oben scrollen", - "Message": { - "pin_message": "Nachricht anpinnen", - "unpin_message": "Nachricht lΓΆsen", - "forwarded_private_message": "Forwarded private message", - "forwarded_from_earlier_in_this_room": "Forwarded from earlier in this room", - "forwarded_from_another_room": "Aus einem anderen Raum weitergeleitet", - "jump_to_original": "jump to original", - "failed_to_send": "Senden fehlgeschlagen.", - "only_you_can_see_this": "Only you can see this.", - "add_reaction": "Add Reaction", - "add_to_user_sticker_pack": "Add to User Sticker Pack", - "reply": "antworten", - "reply_in_thread": "Reply in Thread", - "edit_message": "Edit Message", - "Editor": { - "edit_message": "Edit message..." - } - }, - "Reactions": { - "reacted_with": "Reacted with" - } - }, - "RoomInput": { - "recording_duration": "Aufnahmedauer:" - } -} diff --git a/public/locales/en.json b/public/locales/en.json index ae754c5149..c1006cccc5 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -10,8 +10,8 @@ "authentication_failed_failed_to_setup_device_verification": "Authentication failed! Failed to setup device verification.", "unexpected_error_crypto_module_not_found": "Unexpected Error! Crypto module not found!", "unexpected_error_failed_to_create_recovery_key": "Unexpected Error! Failed to create recovery key.", - "generate_a": "Generate a", - "for_verifying_identity_if_you_do_not_have_access_to_other_devices_additiona": "for verifying identity if you do not have access to other devices. Additionally, setup a passphrase as a memorable alternative.", + "generate_a_recovery_key": "Generate a recovery key for verifying identity if you do not have access to other devices. Additionally, setup a passphrase as a memorable alternative", + "optional_passphrase": "Passphrase (optional)", "authentication_steps_to_perform_this_action_are_not_supported_by_client": "Authentication steps to perform this action are not supported by client.", "store_the_recovery_key_in_a_safe_place_for_future_use_as_you_will_need_it_t": "Store the Recovery Key in a safe place for future use, as you will need it to verify your identity if you do not have access to other devices.", "setup_device_verification": "Setup Device Verification", @@ -65,6 +65,10 @@ "assign_unique_colors_to_users_based_on_their_id_does_not_override_room_spac": "Assign unique colors to users based on their ID. Does not override room/space custom colors. Will override default role colors.", "show_pronoun_pills": "Show Pronoun Pills", "display_user_pronouns_in_the_message_timeline": "Display user pronouns in the message timeline.", + "max_pronoun_pills": "Max Pronoun Pills", + "maximum_number_of_pronoun_pills": "Maximum number of pronoun pills shown per user in the timeline. Additional pronouns appear behind the ... pill.", + "max_pronoun_pill_length": "Max Pronoun Pill Length", + "maximum_pronoun_pill_length": "Maximum characters shown in each pronoun pill before truncation.", "pronoun_pills_for_all": "Pronoun Pills for All", "attempts_to_convert_pronouns_in_names_into_pills_e_g_they_them_or_it_its_tu": "Attempts to convert pronouns in names into pills (e.g. [they/them] or (it/its) turns into a pill).", "render_custom_profile_cards": "Render Custom Profile Cards", @@ -218,10 +222,6 @@ "jump_to_original": "jump to original", "failed_to_send": "Failed to send.", "only_you_can_see_this": "Only you can see this.", - "add_reaction": "Add Reaction", - "add_to_user_sticker_pack": "Add to User Sticker Pack", - "reply": "Reply", - "reply_in_thread": "Reply in Thread", "edit_message": "Edit Message", "Editor": { "edit_message": "Edit message..." @@ -261,11 +261,7 @@ }, "Room": { "DirectInvite": { - "this_is_a": "This is a", - "direct_message": "Direct Message", - "room_intended_for_a_conversation_between_two_persons_would_you_like_to_conv": "room, intended for a conversation between two persons. Would you like to convert it into a", - "group_chat": "group chat", - "before_continuing": "before continuing?", + "direct_message_invite_prompt": "This is a Direct Message room, intended for a conversation between two persons. Would you like to convert it into a group chat before continuing?", "failed_to_convert_direct_message_to_room": "Failed to convert direct message to room!", "convert_to_group_chat_and_invite": "Convert to Group Chat and Invite", "invite_to_direct_message_anyway": "Invite to Direct Message anyway", diff --git a/public/locales/fr.json b/public/locales/fr.json deleted file mode 100644 index ae750caa29..0000000000 --- a/public/locales/fr.json +++ /dev/null @@ -1,118 +0,0 @@ -{ - "Organisms": { - "RoomCommon": { - "changed_room_name": " changed room name" - } - }, - "Settings": { - "device_verification": { - "error_uia_action_without_data": "Unexpected Error! UIA action is perform without data.", - "authentication_failed_failed_to_setup_device_verification": "Authentication failed! Failed to setup device verification.", - "unexpected_error_crypto_module_not_found": "Unexpected Error! Crypto module not found!", - "unexpected_error_failed_to_create_recovery_key": "Unexpected Error! Failed to create recovery key.", - "generate_a": "Generate a", - "for_verifying_identity_if_you_do_not_have_access_to_other_devices_additiona": "for verifying identity if you do not have access to other devices. Additionally, setup a passphrase as a memorable alternative.", - "authentication_steps_to_perform_this_action_are_not_supported_by_client": "Authentication steps to perform this action are not supported by client.", - "store_the_recovery_key_in_a_safe_place_for_future_use_as_you_will_need_it_t": "Store the Recovery Key in a safe place for future use, as you will need it to verify your identity if you do not have access to other devices.", - "setup_device_verification": "Setup Device Verification", - "reset_device_verification": "Reset Device Verification", - "resetting_device_verification_is_permanent": "Resetting device verification is permanent.", - "anyone_you_have_verified_with_will_see_security_alerts_and_your_encryption": "Anyone you have verified with will see security alerts and your encryption backup will be lost. You almost certainly do not want to do this, unless you have lost", - "and_every_device_you_can_verify_from": "and every device you can verify from.", - "please_accept_the_request_from_other_device": "Please accept the request from other device.", - "click_accept_to_start_the_verification_process": "Click accept to start the verification process.", - "waiting_for_request_to_be_accepted": "Waiting for request to be accepted...", - "confirm_the_emoji_below_are_displayed_on_both_devices_in_the_same_order": "Confirm the emoji below are displayed on both devices, in the same order:", - "they_match": "They Match", - "do_not_match": "Do not Match", - "starting_verification_using_emoji_comparison": "Starting verification using emoji comparison...", - "your_device_is_verified": "Your device is verified.", - "verification_has_been_canceled": "Verification has been canceled.", - "device_verification": "Device Verification", - "unexpected_error_verification_is_started_but_verifier_is_missing": "Unexpected Error! Verification is started but verifier is missing.", - "verification_request_has_been_accepted": "Verification request has been accepted.", - "waiting_for_the_response_from_other_device": "Waiting for the response from other device..." - } - }, - "General": { - "continue": "Continue", - "passphrase": "Passphrase", - "optional": "optional", - "unexpected_error": "Unexpected Error!", - "recovery_key": "Recovery Key", - "download": "Download", - "copy": "Copy", - "hide": "Hide", - "show": "Show", - "recovery_passphrase": "Recovery Passphrase", - "reset": "Reset", - "close": "Close", - "accept": "Accept", - "okay": "Okay", - "retry": "Retry", - "user": "User", - "delete": "Delete", - "dismiss": "Dismiss", - "nickname": "Nickname", - "save": "Save", - "clear": "Clear", - "edit_nickname": "Edit Nickname", - "set_nickname": "Set Nickname", - "cancel": "Cancel" - }, - "RoomView": { - "failed_to_load_history": "Failed to load history.", - "failed_to_load_messages": "Failed to load messages.", - "jump_to_unread": "Jump to Unread", - "mark_as_read": "Mark as Read", - "new_messages": "New Messages", - "jump_to_latest": "Jump to Latest", - "call_started_by": "Call started by", - "start_voice_call": "Start Voice Call", - "typing": { - "are_typing": " are typing...", - "typing_sep_word": " and " - }, - "is_typing": "is typing...", - "close_threads": "Close threads", - "search_threads": "Search threads...", - "clear_search": "Clear search", - "Threads": { - "no_threads_match_your_search": "No threads match your search.", - "no_threads_yet": "No threads yet.", - "jump": "Jump", - "threads": "Threads", - "thread": "Thread", - "no_replies_yet_start_the_thread_below": "No replies yet. Start the thread below!" - }, - "join_new_room": "Join New Room", - "this_room_has_been_replaced_and_is_no_longer_active": "This room has been replaced and is no longer active.", - "failed_to_join_replacement_room": "Failed to join replacement room!", - "open_new_room": "Open New Room", - "scroll_to_top": "Scroll to Top", - "Message": { - "pin_message": "Pin Message", - "unpin_message": "Unpin Message", - "forwarded_private_message": "Forwarded private message", - "forwarded_from_earlier_in_this_room": "Forwarded from earlier in this room", - "forwarded_from_another_room": "Forwarded from another room", - "jump_to_original": "jump to original", - "failed_to_send": "Failed to send.", - "only_you_can_see_this": "Only you can see this.", - "add_reaction": "Add Reaction", - "add_to_user_sticker_pack": "Add to User Sticker Pack", - "reply": "Reply", - "reply_in_thread": "Reply in Thread", - "edit_message": "Edit Message", - "Editor": { - "edit_message": "Edit message..." - } - }, - "Reactions": { - "reacted_with": "Reacted with" - } - }, - "RoomInput": { - "recording_duration": "Recording duration:" - } -} diff --git a/public/locales/ro.json b/public/locales/ro.json new file mode 100644 index 0000000000..081ec24f56 --- /dev/null +++ b/public/locales/ro.json @@ -0,0 +1,273 @@ +{ + "Organisms": { + "RoomCommon": { + "changed_room_name": " a schimbat numele camerei" + } + }, + "Settings": { + "device_verification": { + "error_uia_action_without_data": "Eroare neasΜ¦teptataΜ†! Actiunea UIA este facutaΜ† faraΜ† date.", + "authentication_failed_failed_to_setup_device_verification": "Autentificare esΜ¦uataΜ†! Verificarea dispozitivului a esΜ¦uat.", + "unexpected_error_crypto_module_not_found": "Eroare neasΜ¦teptataΜ†! Modulul Crypto nu poate fi accessat!", + "unexpected_error_failed_to_create_recovery_key": "Eroare neasΜ¦teptataΜ†! Incercarea de a crea o cheie de rezervaΜ† a esΜ¦uat.", + "generate_a_recovery_key": "GeneratΜ¦i o cheie de recuperare pentru a iΜ‚tΜ¦i verifica identitatea in cazul in care nu ai access la alte dispozitive. AditΜ¦ional, seteazaΜ† o parolaΜ† ca o alternativaΜ† mai usΜ¦oaraΜ† de amintit.", + "optional_passphrase": "ParolaΜ† (optionalaΜ†)", + "authentication_steps_to_perform_this_action_are_not_supported_by_client": "PasΜ¦i pentru autentificare nu sunt acceptatΜ¦i de aceastaΜ† aplicatΜ¦ie.", + "store_the_recovery_key_in_a_safe_place_for_future_use_as_you_will_need_it_t": "SalveazaΜ† cheia de recuperare intr-un loc sigur pentru a fi accesataΜ† in viitor. Vei avea nevoie de ea sa iΜ‚tΜ¦i verifici identitatea in cazul in care nu ai acces la alte dispozitive.", + "setup_device_verification": "ConfigureazaΜ† verificare dispozitivelor.", + "reset_device_verification": "Reseteaza Identitea de verificare a dispozitivelor", + "resetting_device_verification_is_permanent": "Resetarea verificaΜ†rii dispozitivelor este permanentaΜ†!", + "anyone_you_have_verified_with_will_see_security_alerts_and_your_encryption": "ToataΜ† lumea cu care ai vorbit va primii o alertaΜ† de securitate si encriptia de rezervaΜ† va fi pierdutaΜ†. Aproape sigur nu vrei saΜ† faci asta, singura situatΜ¦ie iΜ‚n care ai vrea saΜ† faci asta este daca ai pierdut", + "and_every_device_you_can_verify_from": "sΜ¦i orice alt dispozitiv cu care ai putea verifica.", + "please_accept_the_request_from_other_device": "AcceptatΜ¦i cererea dintr-un alt dispozitiv.", + "click_accept_to_start_the_verification_process": "Apasa pe 'AcceptaΜ†' pentru a iΜ‚ncepe procesul de verificare.", + "waiting_for_request_to_be_accepted": "AsteptaΜ†m ca cererea saΜ† fie acceptataΜ†...", + "confirm_the_emoji_below_are_displayed_on_both_devices_in_the_same_order": "ConfirmaΜ† ca emoticoanele prezentate ulterior sunt aceleasΜ¦i pe ambele dispozitive, sΜ¦i iΜ‚n aceasΜ¦i ordine:", + "they_match": "Se potrivesc", + "do_not_match": "Sunt diferite", + "starting_verification_using_emoji_comparison": "IΜ‚ncepe verificare prin comparare de emoticoane...", + "your_device_is_verified": "Dispozitivul acesta e verificat.", + "verification_has_been_canceled": "Verificarea a forst opritaΜ†.", + "device_verification": "Verificare dispozitiv", + "unexpected_error_verification_is_started_but_verifier_is_missing": "Eroare neasΜ¦teptataΜ†! Verificarea a iΜ‚nceput dar verificatorul a dispaΜ†rut.", + "verification_request_has_been_accepted": "Cererea de verificare a fost acceptataΜ†.", + "waiting_for_the_response_from_other_device": "AsΜ¦teptaΜ†m pentru un raΜ†spuns de la celaΜ†lalt dispozitiv..." + }, + "PerMessageProfiles": { + "profile_id": "ID profil:", + "display_name": "Nume AfisΜ¦at:" + }, + "Profile": { + "avatar_and_upload": "Imagine de profil si iΜ‚ncaΜ†rcare", + "profile_avatar": "Imagine de profil", + "avatar_fallback": "Imagine de rezervaΜ†", + "upload_avatar_image": "IΜ‚ncarcaΜ† imagine de profil", + "display_name_input": "AfisΜ¦ introducere nume de profil", + "reset_display_name": "ReseteazaΜ† numele afisΜ¦at" + }, + "Cosmetics": { + "light_and_dark": "Luminos & IΜ‚ntunecat", + "light_only": "Doar luminos", + "dark_only": "Doar iΜ‚ntunecat", + "off": "NiciodataΜ†", + "jumbo_emoji": "Emojiuri Jumbo", + "jumbo_emoji_size": "MaΜ†rime Emojiuri Jumbo", + "adjust_the_size_of_emojis_sent_without_text": "SchimbaΜ† maΜ†rimea emojiurilor trimise faΜ‚raΜ† text.", + "privacy_and_security": "ConfidenΘ›ialitate Θ™i Securitate", + "blur_media": "EstompazaΜ† media.", + "blurs_images_and_videos_in_the_timeline": "EstompeazaΜ† imaginile si videourile din mesaje.", + "blur_avatars": "EstompeazaΜ† avatar-uri", + "blurs_user_profile_pictures_and_room_icons": "EstompeazaΜ† imaginile de profil si iconitΜ¦ele camerelor.", + "blur_emotes": "EstompeazaΜ† emoticoane", + "blurs_emoticons_within_messages": "EstompeazaΜ† emoticoanele din mesaje.", + "identity": "Identitate", + "colorful_names": "Nume Colorate Aleatoriu", + "assign_unique_colors_to_users_based_on_their_id_does_not_override_room_spac": "Atribuie culori unici utilizatorilor pe baza ID-ului lor. AceastaΜ† setare nu va inlocuii culorile atribuite de cameraΜ†/spatΜ¦iu, dar va inlocuii culoarea rolului de bazaΜ†", + "show_pronoun_pills": "ArataΜ† pilule de pronume", + "display_user_pronouns_in_the_message_timeline": "Arata pronumele utilizatorilor in mesaje", + "max_pronoun_pills": "NumaΜ†r maxim de pilule de pronume", + "maximum_number_of_pronoun_pills": "NumaΜ†rul maxim de pilule de pronume prezente de persoanaΜ† in conversatie. Alte pronume apar dupaΜ† pilula intitulataΜ† '...'", + "max_pronoun_pill_length": "Lungimea maximaΜ† a pronumelor de profil", + "maximum_pronoun_pill_length": "NumaΜ†rul maxim de litere in fiecare pronume iΜ‚nainte de a fi trunchiataΜ†.", + "pronoun_pills_for_all": "Pilule de pronume pentru toataΜ† lumea", + "attempts_to_convert_pronouns_in_names_into_pills_e_g_they_them_or_it_its_tu": "IΜ‚ncearcaΜ† sa transformi pronumele din nume in pilule (ex. [el/lui] sau [ea/iei] este transformat in pilulaΜ†).", + "render_custom_profile_cards": "RedaΜ† carduri de profil personalizate.", + "choose_whose_profile_card_colors_to_show_everyone_with_a_scheme_only_light": "Alege caΜ‚nd sa se redea cardul de profil: pentru toataΜ† lumea, doar teme iΜ‚ntunecate, doar teme luminoase, sau nu le reda deloc.", + "render_global_username_colors": "RedaΜ† culorile generale numelor persoanelor", + "display_the_username_colors_anyone_can_set_in_their_account_settings": "RedaΜ† culorile numelor tuturor persoanelor care au fost setate de acestea.", + "render_space_room_username_colors": "RedaΜ† culorile numelor setate de CameraΜ†/SpatΜ¦iu.", + "display_the_username_colors_that_can_be_set_with_color": "RedaΜ† culorile numelor care pot fi setate cu /color.", + "render_space_room_fonts": "RedaΜ† fonturile Camerei/SpatΜ¦iului.", + "display_the_username_fonts_that_can_be_set_with_font": "RedaΜ† fontul numelor care pot fi setate cu /font.", + "consistent_icon_style": "Stil al icoanelor consistent.", + "harmonize_icon_appearance_with_background_fill": "ArmonizeazaΜ† forma icoanelor cu o culoare de fundal.", + "extra_small": "Foarte mic", + "none_same_size_as_text": "BazaΜ† (aceasΜ¦i maΜ†rime ca textul)", + "small": "Mic", + "normal": "Normal", + "large": "Mari", + "extra_large": "Foarte mari", + "language_specific_pronouns": "Pronume specifice limbii", + "show_pronouns_only_in_selected_language": "RedaΜ† doar pronumele in limba selectataΜ†", + "if_enabled_pronouns_are_only_shown_when_they_match_your_selected_language_t": "DacaΜ† activat, doar pronumele in limba selectataΜ† de tine vor apaΜ†rea. Asta ajuta dacaΜ† contactele tale au setata pronume in limbi diferite. Nu afecteaza cum pronumele setate de tine sunt iΜ‚mpaΜ†rtaΜ†sΜ¦ite.", + "selected_language_for_pronouns": "Limbi selectate pentru pronume.", + "the_language_to_show_pronouns_for_when_the_above_setting_is_enabled": "Limbile in care pronumele vor fi redate pentru setarea de asupra.", + "language_code_e_g_en_de_en_de": "Codul limbii (ex. 'ro', 'de', 'ro,de')" + }, + "save_bandwidth_for_sticker_and_emoji_images": "ReducetΜ¦i Consumul retΜ¦elei pentru stickere si emoticoane", + "enable_bandwidth_saving_for_stickers_and_emojis": "Porneste salvare de 'bandwidth' pentru stickere si emoticoane", + "if_enabled_sticker_and_emoji_images_will_be_optimized_to_save_bandwidth_thi": "DacaΜ† pornit, stickerele si emoticoanele vor fi optimizate pentru a consuma mai putΜ¦in internet. Asta ajutaΜ† consumul de date caΜ‚nd vaΜ†zaΜ†nd aceste imagini, dar cresΜ¦te costul de computare a server-ului.", + "personas_per_message_profiles": "PersonalitaΜ†tΜ¦i. (Profiluri per-mesaj)", + "show_personas_tab": "PrezintaΜ† meniul de PersonalitaΜ†tΜ¦i", + "enables_the_personas_tab_in_the_settings_menu_for_per_message_profiles": "Adauga meniul de personalitaΜ†tΜ¦i in bara de setaΜ†ri pentru profile specifice fiecaΜ†rui mesaj.", + "experimental": "Experimental", + "the_features_listed_below_may_be_unstable_or_incomplete": "OptΜ¦iunile listate ulterior pot fi instabile sau incomplete,", + "use_at_your_own_risk": "folosesΜ¦te la riscul vostru", + "please_report_any_new_issues_potentially_caused_by_these_features": "VaΜ† rugaΜ†m sa raportati orice probleme noi cauzate potentΜ¦ial de aceste optΜ¦iuni! Mersi!", + "enable_sharing_of_encrypted_history": "ActiveazaΜ† iΜ‚mpaΜ†rtaΜ†sΜ¦irea de Istorie CriptataΜ†.", + "enable_the_sharehistory_command": "ActiveazaΜ† comanda de /sharehistory", + "if_enabled_this_command_will_allow_users_to_share_encrypted_history_with_ot": "DacaΜ† activataΜ†, aceastaΜ† optiune va permite utilizatorilor saΜ† impaΜ†rtasΜ¦eascaΜ† istoria criptataΜ† cu altΜ¦i utilizatori noi, per MSC4268.", + "disable_sharehistory_command": "DezactiveazaΜ† comanda /sharehistory", + "enable_sharehistory_command": "ActiveazaΜ† comanda /sharehistory", + "General": { + "formatting": "Formatare", + "month": "Luna", + "day_of_the_week": "Ziua saΜ†ptaΜ†maΜ†nii", + "day_of_the_week_sunday_0": "Ziua saΜ†ptaΜ†maΜ†nii (DuminicaΜ† = 0)", + "two_letter_day_name": "Numele zilei cu 2 litere", + "short_day_name": "Numele zilei scurt", + "full_day_name": "Numele zilei iΜ‚ntreg", + "day_of_the_month": "Ziua din lunaΜ†", + "full_month_name": "Numele iΜ‚ntreg al lunii", + "short_month_name": "Numele scurt al lunii", + "the_month": "Luna", + "two_digit_month": "Luna cu 2 cifre", + "four_digit_year": "Anul cu 4 cifre", + "two_digit_year": "Anul cu 2 cifre" + }, + "KeyboardShortcuts": { + "jump_to_the_highest_priority_unread_room": "Dute la camera cu cea mai ridicata prioritate de necitire", + "go_to_next_unread_room_cycle": "Dute la urmaΜ†toarea camera necititaΜ† (urmeazaΜ† un ciclu)", + "go_to_previous_unread_room_cycle": "Go to previous unread room (urmeazaΜ† un ciclu)", + "undo_in_message_editor": "SΜ¦terge iΜ‚n editorul de mesaje", + "redo_in_message_editor": "Reface iΜ‚n editorul de mesaje", + "bold": "IΜ‚ngrosΜ¦at", + "italic": "Italic", + "underline": "Subliniat", + "navigation": "NavigatΜ¦ie" + }, + "Persona": { + "limited_compatibility_with_pluralkit_like_functions": "Compatibilitate limitataΜ† cu functii similare cu PluralKit", + "enable_pk_commands": "ActiveazaΜ† comenzi PK", + "if_enabled_it_will_enable_a_few_pk_style_commands_currently_verry_limited": "DacaΜ† activat, va permite folosirea de comenzi in stil PK, deocamdataΜ† foarte limitate", + "disable_pk_commands": "dezactiveazaΜ† comenzile pk;", + "enable_pks_commands": "activeazaΜ† comenzile pk;", + "enable_shorthands": "ActiveazaΜ† scurtaΜ†turi", + "if_enabled_you_can_use_shorthands_to_use_a_persona_for_one_message_only_eg": "DacaΜ† activat, vei putea folosii scuraturi pentru a folosi personalitaΜ†tΜ¦i pentru un mesaj (ex. '✨:test')", + "disable_checking_typed_messages_for_shorthands": "dezactiveazaΜ† verificarea mesajelor scrise pentru scurataturi", + "enable_checking_typed_messages_for_shorthands": "activeaza verificarea mesajelor scrise pentru scurataturi" + } + }, + "General": { + "continue": "ContinuaΜ†", + "passphrase": "ParolaΜ†", + "optional": "optΜ¦ional", + "unexpected_error": "Eroare NeasΜ¦teptataΜ†!", + "recovery_key": "Cheie de Recuperare", + "download": "DescarcaΜ†", + "copy": "CopiazaΜ†", + "hide": "Ascunde", + "show": "ArataΜ†", + "recovery_passphrase": "ParolaΜ† de recuperare", + "reset": "ReseteazaΜ†", + "close": "IΜ‚nchide", + "accept": "Accept", + "okay": "Okay", + "retry": "ReiΜ‚ncearcaΜ†", + "user": "utilizator", + "delete": "SΜ¦terge", + "dismiss": "Omite", + "nickname": "PoreclaΜ†", + "save": "SalveazaΜ†", + "clear": "CuraΜ†tΜ¦aΜ†", + "edit_nickname": "EditeazaΜ† PoreclaΜ†", + "set_nickname": "SeteazaΜ† PoreclaΜ†", + "cancel": "AnuleazaΜ†", + "upload": "IΜ‚ncarcaΜ†", + "upload_area": "ZonaΜ† iΜ‚ncaΜ†rcare", + "display_name": "Nume AfisΜ¦at", + "pronouns": "Pronume", + "unknown": "Necunoscut", + "edit": "EditeazaΜ†" + }, + "RoomView": { + "failed_to_load_history": "IΜ‚ncaΜ†rcarea istoriei a esΜ¦uat.", + "failed_to_load_messages": "IΜ‚ncaΜ†rcarea mesajelor a esΜ¦uat.", + "jump_to_unread": "Sari la necitit", + "mark_as_read": "NoteazaΜ† ca Citit", + "new_messages": "Mesaje Noi", + "jump_to_latest": "Sari la ultimele mesaje", + "call_started_by": "Apel iΜ‚nceput de", + "start_voice_call": "IΜ‚ncepe apel audio", + "typing": { + "are_typing": " scriu...", + "typing_sep_word": " sΜ¦i " + }, + "is_typing": "scrie...", + "close_threads": "IΜ‚nchide fire", + "search_threads": "CautaΜ† fire...", + "clear_search": "CuraΜ†tΜ¦aΜ† caΜ†utarea", + "Threads": { + "no_threads_match_your_search": "Nici un fir nu se potrivesΜ¦te cautaΜ†rii.", + "no_threads_yet": "IΜ‚nca nu sunt fire.", + "jump": "Sari", + "threads": "Fire", + "thread": "Fir", + "no_replies_yet_start_the_thread_below": "IΜ‚ncaΜ† nu are raΜ†spunsuri. IΜ‚ncepe un fir de mesaje!" + }, + "join_new_room": "AlaΜ†turaΜ†te unei camere noi", + "this_room_has_been_replaced_and_is_no_longer_active": "AceasaΜ† camera a fost schimbataΜ† sΜ¦i nu mai este activaΜ†.", + "failed_to_join_replacement_room": "IΜ‚ncercarea de a te alaΜ†tura camerei noi a esΜ¦uat!", + "open_new_room": "Deschide CameraΜ† nouaΜ†", + "scroll_to_top": "DeruleazaΜ† iΜ‚n Sus", + "Message": { + "pin_message": "FixeazaΜ† Mesajul", + "unpin_message": "Scoata fixarea Mesajului", + "forwarded_private_message": "Mesaj privat redirectΜ¦ionat", + "forwarded_from_earlier_in_this_room": "RedirectΜ¦ionat dintr-un mesaj mai vechi din aceastaΜ† cameraΜ†", + "forwarded_from_another_room": "RedirectΜ¦ionat din altaΜ† cameraΜ†", + "jump_to_original": "dute la original", + "failed_to_send": "Trimitere esΜ¦uataΜ†.", + "only_you_can_see_this": "Doar tu potΜ¦i vedea asta.", + "edit_message": "EditeazaΜ† mesajul", + "Editor": { + "edit_message": "EditeazaΜ† mesajul..." + } + }, + "Reactions": { + "reacted_with": "ReactΜ¦ionat cu" + } + }, + "RoomInput": { + "recording_duration": "Lungime iΜ‚nregistrare:", + "EmojiBoard": { + "no_results_found": "Nu un rezultat", + "search_results": "Rezultate caΜ†utare", + "personal_pack": "Pachet Personal", + "unknown_pack": "Pachet Necunoscut" + } + }, + "RoomCreate": { + "versions": "Versiuni", + "founders": "Fondatori", + "special_privileged_users_can_be_assigned_during_creation_these_users_have_e": "In durata creaΜ†ri, anumite persoane pot fi privilegiate. Aceste persoana au un control ridicat si pot fi modificate doar cu o modernizare a camerei.", + "no_suggestions": "Nici o sugestie", + "please_provide_the_user_id_and_hit_enter": "Oferiti ID-ul de utilizator si apaΜ†satΜ¦i Enter.", + "only_member_of_parent_space_can_join": "Doar membrii a spatΜ¦iului paΜ†rinte se pot alaΜ†tura.", + "private": "PrivataΜ†", + "only_people_with_invite_can_join": "Doar persoanele cu o invitatΜ¦ie se pot alaΜ†tura.", + "anyone_with_the_address_can_join": "Oricine are adresa se poate alaΜ†tura.", + "public": "PublicaΜ†", + "address_optional": "AdresaΜ† (OptΜ¦ionalaΜ†)", + "pick_an_unique_address_to_make_it_discoverable": "Alege o adresaΜ† unicaΜ† saΜ† poataΜ† fi comunitatea descoperitaΜ†.", + "this_address_is_already_taken_please_select_a_different_one": "AceastaΜ† adresaΜ† este deja luataΜ†. SelectatΜ¦i una diferitaΜ†.", + "voice_room": "CameraΜ† vocalaΜ†", + "live_audio_and_video_conversations": "; conversatΜ¦ii audio-video live.", + "chat_room": "CameraΜ† discutΜ¦ii", + "messages_photos_and_videos": "; Mesaje, fotografii, and videoclipuri." + }, + "Room": { + "DirectInvite": { + "direct_message_invite_prompt": "Aceasa este o conversatΜ¦ie directaΜ†, menitaΜ† pentru discutΜ¦ii iΜ‚ntre douaΜ† persoane, DoritΜ¦i sa o tranformatΜ¦i intr-un group de conversatΜ¦ie iΜ‚nainte de a continua?", + "failed_to_convert_direct_message_to_room": "Transformarea convesatΜ¦iei directe in conversatΜ¦ie de grup a esΜ¦uat!", + "convert_to_group_chat_and_invite": "TransformaΜ† in grup sΜ¦i invitaΜ†", + "invite_to_direct_message_anyway": "InvitaΜ† la mesaje directe oricum", + "invite_another_member": "InvitaΜ† altaΜ† persoana" + } + }, + "DevTools": { + "json_content": "ContΜ¦inut JSON", + "account_data": "Datele contului", + "developer_tools": "Unelete Dezvoltatori" + } +} diff --git a/src/app/components/DeviceVerificationSetup.tsx b/src/app/components/DeviceVerificationSetup.tsx index 599bcd7036..3ce6be1463 100644 --- a/src/app/components/DeviceVerificationSetup.tsx +++ b/src/app/components/DeviceVerificationSetup.tsx @@ -227,14 +227,11 @@ function SetupVerification({ onComplete }: Readonly) { return ( - {t('Settings.device_verification.generate_a')} {t('General.recovery_key')}{' '} - {t( - 'Settings.device_verification.for_verifying_identity_if_you_do_not_have_access_to_other_devices_additiona' - )} + {t('Settings.device_verification.generate_a_recovery_key')} - {t('General.passphrase')} ({t('General.optional')}) + {t('Settings.device_verification.optional_passphrase')} @@ -402,7 +399,7 @@ export const DeviceVerificationReset = forwardRef{t('General.recovery_key')} or {t('General.recovery_passphrase')}{' '} + {t('General.recovery_key')} / {t('General.recovery_passphrase')}{' '} {t('Settings.device_verification.and_every_device_you_can_verify_from')} diff --git a/src/app/components/direct-invite-prompt/DirectInvitePrompt.tsx b/src/app/components/direct-invite-prompt/DirectInvitePrompt.tsx index 9ee8a03eb9..b5e91953d3 100644 --- a/src/app/components/direct-invite-prompt/DirectInvitePrompt.tsx +++ b/src/app/components/direct-invite-prompt/DirectInvitePrompt.tsx @@ -61,14 +61,7 @@ export function DirectInvitePrompt({
- - {t('Room.DirectInvite.this_is_a')} {t('Room.DirectInvite.direct_message')}{' '} - {t( - 'Room.DirectInvite.room_intended_for_a_conversation_between_two_persons_would_you_like_to_conv' - )}{' '} - {t('Room.DirectInvite.group_chat')}{' '} - {t('Room.DirectInvite.before_continuing')} - + {t('Room.DirectInvite.direct_message_invite_prompt')} {convertError && ( {t('Room.DirectInvite.failed_to_convert_direct_message_to_room')} {convertError} diff --git a/src/app/features/room/RoomCallButton.test.tsx b/src/app/features/room/RoomCallButton.test.tsx index 7f4ce6ae6c..6fc10e3b19 100644 --- a/src/app/features/room/RoomCallButton.test.tsx +++ b/src/app/features/room/RoomCallButton.test.tsx @@ -3,6 +3,7 @@ import { describe, expect, it, vi, beforeEach } from 'vitest'; import type * as JotaiModule from 'jotai'; import type { Room } from '$types/matrix-sdk'; import { RoomCallButton } from './RoomCallButton'; +import { t } from 'i18next'; const { startCallMock, useCallJoinedMock } = vi.hoisted(() => ({ startCallMock: vi.fn<(...args: unknown[]) => void>(), @@ -40,7 +41,7 @@ describe('RoomCallButton', () => { /> ); - fireEvent.click(screen.getByRole('button', { name: /start voice call/i })); + fireEvent.click(screen.getByRole('button', { name: t('RoomView.start_voice_call') })); await waitFor(() => { expect(startCallMock).toHaveBeenCalledWith(room, { @@ -61,7 +62,7 @@ describe('RoomCallButton', () => { /> ); - fireEvent.click(screen.getByRole('button', { name: /start video call/i })); + fireEvent.click(screen.getByRole('button', { name: t('RoomView.start_voice_call') })); await waitFor(() => { expect(startCallMock).toHaveBeenCalledWith(room, { diff --git a/src/app/features/settings/cosmetics/Cosmetics.tsx b/src/app/features/settings/cosmetics/Cosmetics.tsx index e412af9e1c..0cf62439b5 100644 --- a/src/app/features/settings/cosmetics/Cosmetics.tsx +++ b/src/app/features/settings/cosmetics/Cosmetics.tsx @@ -28,7 +28,7 @@ import { SequenceCardStyle } from '$features/settings/styles.css'; import { SettingsSectionPage } from '../SettingsSectionPage'; import { Appearance } from './Themes'; import { LanguageSpecificPronouns } from './LanguageSpecificPronouns'; -import { t } from 'i18next'; +import { useTranslation } from 'react-i18next'; function PronounPillMaxCountInput({ disabled }: { disabled: boolean }) { const [maxCount, setMaxCount] = useSetting(settingsAtom, 'pronounPillMaxCount'); @@ -207,16 +207,18 @@ function IconSizeSettings() { ); } -const emojiSizeItems = [ - { id: 'none', name: t('Settings.Cosmetics.none_same_size_as_text') }, - { id: 'extraSmall', name: t('Settings.Cosmetics.extra_small') }, - { id: 'small', name: t('Settings.Cosmetics.small') }, - { id: 'normal', name: t('Settings.Cosmetics.normal') }, - { id: 'large', name: t('Settings.Cosmetics.large') }, - { id: 'extraLarge', name: t('Settings.Cosmetics.extra_large') }, -]; - function SelectJumboEmojiSize() { + const { t } = useTranslation(); + + const emojiSizeItems = [ + { id: 'none', name: t('Settings.Cosmetics.none_same_size_as_text') }, + { id: 'extraSmall', name: t('Settings.Cosmetics.extra_small') }, + { id: 'small', name: t('Settings.Cosmetics.small') }, + { id: 'normal', name: t('Settings.Cosmetics.normal') }, + { id: 'large', name: t('Settings.Cosmetics.large') }, + { id: 'extraLarge', name: t('Settings.Cosmetics.extra_large') }, + ]; + const [menuCords, setMenuCords] = useState(); const [jumboEmojiSize, setJumboEmojiSize] = useSetting(settingsAtom, 'jumboEmojiSize'); @@ -284,14 +286,15 @@ function SelectJumboEmojiSize() { ); } -const profileCardRenderItems: { id: RenderUserCardsMode; name: string }[] = [ - { id: 'both', name: t('Settings.Cosmetics.light_and_dark') }, - { id: 'light', name: t('Settings.Cosmetics.light_only') }, - { id: 'dark', name: t('Settings.Cosmetics.dark_only') }, - { id: 'none', name: t('Settings.Cosmetics.off') }, -]; - function SelectRenderCustomProfileCards() { + const { t } = useTranslation(); + const profileCardRenderItems: { id: RenderUserCardsMode; name: string }[] = [ + { id: 'both', name: t('Settings.Cosmetics.light_and_dark') }, + { id: 'light', name: t('Settings.Cosmetics.light_only') }, + { id: 'dark', name: t('Settings.Cosmetics.dark_only') }, + { id: 'none', name: t('Settings.Cosmetics.off') }, + ]; + const [menuCords, setMenuCords] = useState(); const [renderUserCardsMode, setRenderUserCardsMode] = useSetting(settingsAtom, 'renderUserCards'); @@ -362,6 +365,7 @@ function SelectRenderCustomProfileCards() { } function JumboEmoji() { + const { t } = useTranslation(); return ( {t('Settings.Cosmetics.jumbo_emoji')} @@ -378,6 +382,7 @@ function JumboEmoji() { } function Privacy() { + const { t } = useTranslation(); const [privacyBlur, setPrivacyBlur] = useSetting(settingsAtom, 'privacyBlur'); const [privacyBlurAvatars, setPrivacyBlurAvatars] = useSetting( settingsAtom, @@ -424,6 +429,7 @@ function Privacy() { } function IdentityCosmetics() { + const { t } = useTranslation(); const [legacyUsernameColor, setLegacyUsernameColor] = useSetting( settingsAtom, 'legacyUsernameColor' @@ -472,9 +478,9 @@ function IdentityCosmetics() { style={{ opacity: showPronouns ? 1 : 0.5 }} > } /> @@ -485,9 +491,9 @@ function IdentityCosmetics() { style={{ opacity: showPronouns ? 1 : 0.5 }} > } /> diff --git a/src/app/features/settings/general/General.tsx b/src/app/features/settings/general/General.tsx index baa7303466..259e281015 100644 --- a/src/app/features/settings/general/General.tsx +++ b/src/app/features/settings/general/General.tsx @@ -57,6 +57,9 @@ import { exportSettingsAsJson, importSettingsFromJson } from '$utils/settingsSyn import { SettingsSectionPage } from '../SettingsSectionPage'; import { t } from 'i18next'; import { CallSoundSettings } from './CallSoundSettings'; +import { useTranslation } from 'react-i18next'; +import type { SettingMenuOption } from '$components/setting-menu-selector'; +import { SettingMenuSelector } from '$components/setting-menu-selector'; type DateHintProps = { hasChanges: boolean; @@ -431,6 +434,50 @@ function DateAndTime() { ); } +function LanguageChange() { + const { i18n } = useTranslation(); + + const languageOptions: SettingMenuOption[] = [ + { value: '', label: 'System' }, + { value: 'en', label: 'English' }, + { value: 'ro', label: 'RomaΜ‚naΜ†' }, + ]; + const [curLanguage, setCurLanguage] = useState(localStorage.getItem('i18nextLng') ?? ''); + + const handleLanguageChange = (language: string) => { + if (language) { + setCurLanguage(language); + i18n.changeLanguage(language); + } else { + localStorage.removeItem('i18nextLng'); + setCurLanguage(''); + + const detected = i18n.services.languageDetector?.detect(); + i18n.changeLanguage(Array.isArray(detected) ? detected[0] : (detected ?? 'en')); + } + window.location.reload(); + }; + + return ( + + Language + + + } + /> + + + ); +} + function Editor({ isMobile }: Readonly<{ isMobile: boolean }>) { const [enterForNewline, setEnterForNewline] = useSetting(settingsAtom, 'enterForNewline'); const [editorToolbar, setEditorToolbar] = useSetting(settingsAtom, 'editorToolbar'); @@ -1675,6 +1722,7 @@ export function General({ requestBack, requestClose }: Readonly) { + diff --git a/src/app/features/settings/settingsLink.ts b/src/app/features/settings/settingsLink.ts index e39fc9b33b..3d6dfc228b 100644 --- a/src/app/features/settings/settingsLink.ts +++ b/src/app/features/settings/settingsLink.ts @@ -61,6 +61,7 @@ const settingsLinkFocusIdsBySection: Record({ // Prefer the browser / navigator language and avoid using cached localStorage value detection: { - // prefer querystring first (e.g. ?lng=de), then navigator, then html tag, path, subdomain - order: ['querystring', 'navigator', 'htmlTag', 'path', 'subdomain'], + // prefer querystring first (e.g. ?lng=de), then storage,System then navigator, then html tag, path, subdomain + order: ['querystring', 'localStorage', 'navigator', 'htmlTag', 'path', 'subdomain'], lookupQuerystring: 'lng', // do not cache the detected language in localStorage to avoid stale overrides - caches: [], + caches: ['localStorage'], }, debug: false, fallbackLng: 'en', From 1c40340feb2692dc66895c144d87ebaf9af26682 Mon Sep 17 00:00:00 2001 From: Shea Date: Fri, 17 Jul 2026 18:26:00 +0300 Subject: [PATCH 22/23] separated many localization items Signed-off-by: Shea --- .vscode/settings.json | 11 +- crowdin.yml | 3 - knip.json | 2 +- public/locales/en.json | 276 ------------------ public/locales/en/general.json | 169 +++++++++++ public/locales/en/room/create.json | 19 ++ public/locales/en/room/direct/invite.json | 7 + .../en/settings/device_verification.json | 28 ++ public/locales/en/settings/experimental.json | 22 ++ .../en/settings/keyboard_shortcuts.json | 18 ++ public/locales/en/settings/persona.json | 21 ++ public/locales/en/settings/profile.json | 8 + public/locales/ro.json | 273 ----------------- public/locales/ro/general.json | 166 +++++++++++ public/locales/ro/room/create.json | 19 ++ public/locales/ro/room/direct/invite.json | 7 + .../ro/settings/device_verification.json | 28 ++ public/locales/ro/settings/experimental.json | 22 ++ .../ro/settings/keyboard_shortcuts.json | 16 + public/locales/ro/settings/persona.json | 21 ++ public/locales/ro/settings/profile.json | 8 + .../components/DeviceVerificationSetup.tsx | 94 +++--- .../create-room/AdditionalCreatorInput.tsx | 13 +- .../create-room/CreateRoomAccessSelector.tsx | 13 +- .../DirectInvitePrompt.tsx | 17 +- src/app/components/message/Reply.tsx | 2 +- src/app/features/room/RoomViewTyping.tsx | 2 +- .../features/settings/Persona/PKCompat.tsx | 25 +- .../Persona/PerMessageProfileEditor.tsx | 65 +++-- .../features/settings/cosmetics/Cosmetics.tsx | 10 +- .../experimental/BandwithSavingEmojis.tsx | 9 +- .../settings/experimental/Experimental.tsx | 20 +- .../experimental/MSC4268HistoryShare.tsx | 13 +- .../experimental/MSC4274MediaGalleries.tsx | 12 +- src/app/features/settings/general/General.tsx | 2 +- .../keyboard-shortcuts/KeyboardShortcuts.tsx | 58 ++-- .../timeline/useTimelineEventRenderer.tsx | 2 +- src/app/i18n.ts | 20 +- 38 files changed, 769 insertions(+), 752 deletions(-) delete mode 100644 crowdin.yml delete mode 100644 public/locales/en.json create mode 100644 public/locales/en/general.json create mode 100644 public/locales/en/room/create.json create mode 100644 public/locales/en/room/direct/invite.json create mode 100644 public/locales/en/settings/device_verification.json create mode 100644 public/locales/en/settings/experimental.json create mode 100644 public/locales/en/settings/keyboard_shortcuts.json create mode 100644 public/locales/en/settings/persona.json create mode 100644 public/locales/en/settings/profile.json delete mode 100644 public/locales/ro.json create mode 100644 public/locales/ro/general.json create mode 100644 public/locales/ro/room/create.json create mode 100644 public/locales/ro/room/direct/invite.json create mode 100644 public/locales/ro/settings/device_verification.json create mode 100644 public/locales/ro/settings/experimental.json create mode 100644 public/locales/ro/settings/keyboard_shortcuts.json create mode 100644 public/locales/ro/settings/persona.json create mode 100644 public/locales/ro/settings/profile.json diff --git a/.vscode/settings.json b/.vscode/settings.json index e3d39b03bd..f8e70cf7a1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -15,16 +15,17 @@ }, "nixEnvSelector.nixFile": "${workspaceFolder}/flake.nix", "nixEnvSelector.useFlakes": true, + "i18n-ally.localesPaths": ["public/locales"], - "i18n-ally.dirStructure": "auto", + "i18n-ally.namespace": true, + "i18n-ally.pathMatcher": "{locale}/{namespaces}.json", "i18n-ally.enabledFrameworks": ["react", "react-i18next"], + "i18n-ally.keystyle": "nested", "i18n-ally.extract.keyMaxLength": 75, "i18n-ally.extract.targetPickingStrategy": "most-similar", - "i18n-ally.keystyle": "nested", "i18n-ally.extract.keygenStyle": "snake_case", - "i18n-ally.extract.ignoredByFiles": { - "src/app/components/DeviceVerificationSetup.tsx": ["Column"] - }, + "i18n-ally.extract.ignored": ["@"], + "explorer.fileNesting.enabled": true, "explorer.fileNesting.patterns": { "package.json": "pnpm-*.yaml, yarn.lock, package-lock.json, .npmrc, .nvmrc, .node-version", diff --git a/crowdin.yml b/crowdin.yml deleted file mode 100644 index 4c4d36d24a..0000000000 --- a/crowdin.yml +++ /dev/null @@ -1,3 +0,0 @@ -files: - - source: /public/locales/en.json - translation: /public/locales/%two_letters_code%.json diff --git a/knip.json b/knip.json index 1912dfad95..a542824788 100644 --- a/knip.json +++ b/knip.json @@ -1,7 +1,7 @@ { "$schema": "https://unpkg.com/knip@6/schema.json", "entry": ["src/sw.ts", "scripts/normalize-imports.js"], - "ignore": ["oxlint.config.ts", "oxfmt.config.ts"], + "ignore": ["oxlint.config.ts", "oxfmt.config.ts", "i18next.config.ts"], "ignoreExportsUsedInFile": { "interface": true, "type": true diff --git a/public/locales/en.json b/public/locales/en.json deleted file mode 100644 index c1006cccc5..0000000000 --- a/public/locales/en.json +++ /dev/null @@ -1,276 +0,0 @@ -{ - "Organisms": { - "RoomCommon": { - "changed_room_name": " changed room name" - } - }, - "Settings": { - "device_verification": { - "error_uia_action_without_data": "Unexpected Error! UIA action is perform without data.", - "authentication_failed_failed_to_setup_device_verification": "Authentication failed! Failed to setup device verification.", - "unexpected_error_crypto_module_not_found": "Unexpected Error! Crypto module not found!", - "unexpected_error_failed_to_create_recovery_key": "Unexpected Error! Failed to create recovery key.", - "generate_a_recovery_key": "Generate a recovery key for verifying identity if you do not have access to other devices. Additionally, setup a passphrase as a memorable alternative", - "optional_passphrase": "Passphrase (optional)", - "authentication_steps_to_perform_this_action_are_not_supported_by_client": "Authentication steps to perform this action are not supported by client.", - "store_the_recovery_key_in_a_safe_place_for_future_use_as_you_will_need_it_t": "Store the Recovery Key in a safe place for future use, as you will need it to verify your identity if you do not have access to other devices.", - "setup_device_verification": "Setup Device Verification", - "reset_device_verification": "Reset Device Verification", - "resetting_device_verification_is_permanent": "Resetting device verification is permanent.", - "anyone_you_have_verified_with_will_see_security_alerts_and_your_encryption": "Anyone you have verified with will see security alerts and your encryption backup will be lost. You almost certainly do not want to do this, unless you have lost", - "and_every_device_you_can_verify_from": "and every device you can verify from.", - "please_accept_the_request_from_other_device": "Please accept the request from other device.", - "click_accept_to_start_the_verification_process": "Click accept to start the verification process.", - "waiting_for_request_to_be_accepted": "Waiting for request to be accepted...", - "confirm_the_emoji_below_are_displayed_on_both_devices_in_the_same_order": "Confirm the emoji below are displayed on both devices, in the same order:", - "they_match": "They Match", - "do_not_match": "Do not Match", - "starting_verification_using_emoji_comparison": "Starting verification using emoji comparison...", - "your_device_is_verified": "Your device is verified.", - "verification_has_been_canceled": "Verification has been canceled.", - "device_verification": "Device Verification", - "unexpected_error_verification_is_started_but_verifier_is_missing": "Unexpected Error! Verification is started but verifier is missing.", - "verification_request_has_been_accepted": "Verification request has been accepted.", - "waiting_for_the_response_from_other_device": "Waiting for the response from other device..." - }, - "PerMessageProfiles": { - "profile_id": "Profile ID:", - "display_name": "Display Name:" - }, - "Profile": { - "avatar_and_upload": "Avatar and upload", - "profile_avatar": "Profile avatar", - "avatar_fallback": "Avatar fallback", - "upload_avatar_image": "Upload avatar image", - "display_name_input": "Display name input", - "reset_display_name": "Reset display name" - }, - "Cosmetics": { - "light_and_dark": "Light & dark", - "light_only": "Light only", - "dark_only": "Dark only", - "off": "Off", - "jumbo_emoji": "Jumbo Emoji", - "jumbo_emoji_size": "Jumbo Emoji Size", - "adjust_the_size_of_emojis_sent_without_text": "Adjust the size of emojis sent without text.", - "privacy_and_security": "Privacy & Security", - "blur_media": "Blur Media", - "blurs_images_and_videos_in_the_timeline": "Blurs images and videos in the timeline.", - "blur_avatars": "Blur Avatars", - "blurs_user_profile_pictures_and_room_icons": "Blurs user profile pictures and room icons.", - "blur_emotes": "Blur Emotes", - "blurs_emoticons_within_messages": "Blurs emoticons within messages.", - "identity": "Identity", - "colorful_names": "Colorful Names", - "assign_unique_colors_to_users_based_on_their_id_does_not_override_room_spac": "Assign unique colors to users based on their ID. Does not override room/space custom colors. Will override default role colors.", - "show_pronoun_pills": "Show Pronoun Pills", - "display_user_pronouns_in_the_message_timeline": "Display user pronouns in the message timeline.", - "max_pronoun_pills": "Max Pronoun Pills", - "maximum_number_of_pronoun_pills": "Maximum number of pronoun pills shown per user in the timeline. Additional pronouns appear behind the ... pill.", - "max_pronoun_pill_length": "Max Pronoun Pill Length", - "maximum_pronoun_pill_length": "Maximum characters shown in each pronoun pill before truncation.", - "pronoun_pills_for_all": "Pronoun Pills for All", - "attempts_to_convert_pronouns_in_names_into_pills_e_g_they_them_or_it_its_tu": "Attempts to convert pronouns in names into pills (e.g. [they/them] or (it/its) turns into a pill).", - "render_custom_profile_cards": "Render Custom Profile Cards", - "choose_whose_profile_card_colors_to_show_everyone_with_a_scheme_only_light": "Choose whose profile card colors to show: everyone with a scheme, only light or dark schemes, or hide them.", - "render_global_username_colors": "Render Global Username Colors", - "display_the_username_colors_anyone_can_set_in_their_account_settings": "Display the username colors anyone can set in their account settings.", - "render_space_room_username_colors": "Render Space/Room Username Colors", - "display_the_username_colors_that_can_be_set_with_color": "Display the username colors that can be set with /color.", - "render_space_room_fonts": "Render Space/Room Fonts", - "display_the_username_fonts_that_can_be_set_with_font": "Display the username fonts that can be set with /font.", - "consistent_icon_style": "Consistent Icon Style", - "harmonize_icon_appearance_with_background_fill": "Harmonize icon appearance with background fill", - "extra_small": "Extra Small", - "none_same_size_as_text": "None (Same size as text)", - "small": "Small", - "normal": "Normal", - "large": "Large", - "extra_large": "Extra Large", - "language_specific_pronouns": "Language Specific Pronouns", - "show_pronouns_only_in_selected_language": "Show pronouns only in selected language", - "if_enabled_pronouns_are_only_shown_when_they_match_your_selected_language_t": "If enabled, pronouns are only shown when they match your selected language. This helps if your contacts set pronouns in different languages. It doesn't affect how your pronouns are shared with others.", - "selected_language_for_pronouns": "Selected language for pronouns", - "the_language_to_show_pronouns_for_when_the_above_setting_is_enabled": "The language to show pronouns for when the above setting is enabled.", - "language_code_e_g_en_de_en_de": "Language code (e.g. 'en', 'de', 'en,de')" - }, - "save_bandwidth_for_sticker_and_emoji_images": "Save Bandwidth for Sticker and Emoji Images", - "enable_bandwidth_saving_for_stickers_and_emojis": "Enable bandwidth saving for stickers and emojis", - "if_enabled_sticker_and_emoji_images_will_be_optimized_to_save_bandwidth_thi": "If enabled, sticker and emoji images will be optimized to save bandwidth. This helps reduce data usage when viewing these images. But will increase server computation load.", - "personas_per_message_profiles": "Personas (Per-Message Profiles)", - "show_personas_tab": "Show Personas Tab", - "enables_the_personas_tab_in_the_settings_menu_for_per_message_profiles": "Enables the personas tab in the settings menu for per-message profiles", - "experimental": "Experimental", - "the_features_listed_below_may_be_unstable_or_incomplete": "The features listed below may be unstable or incomplete,", - "use_at_your_own_risk": "use at your own risk", - "please_report_any_new_issues_potentially_caused_by_these_features": "Please report any new issues potentially caused by these features!", - "enable_sharing_of_encrypted_history": "Enable Sharing of Encrypted History", - "enable_the_sharehistory_command": "Enable the /sharehistory command", - "if_enabled_this_command_will_allow_users_to_share_encrypted_history_with_ot": "If enabled, this command will allow users to share encrypted history with other newly joined users, as per MSC4268.", - "disable_sharehistory_command": "Disable /sharehistory command", - "enable_sharehistory_command": "Enable /sharehistory command", - "General": { - "formatting": "Formatting", - "month": "Month", - "day_of_the_week": "Day of the Week", - "day_of_the_week_sunday_0": "Day of the week (Sunday = 0)", - "two_letter_day_name": "Two-letter day name", - "short_day_name": "Short day name", - "full_day_name": "Full day name", - "day_of_the_month": "Day of the Month", - "full_month_name": "Full month name", - "short_month_name": "Short month name", - "the_month": "The month", - "two_digit_month": "Two-digit month", - "four_digit_year": "Four-digit year", - "two_digit_year": "Two-digit year" - }, - "KeyboardShortcuts": { - "jump_to_the_highest_priority_unread_room": "Jump to the highest-priority unread room", - "go_to_next_unread_room_cycle": "Go to next unread room (cycle)", - "go_to_previous_unread_room_cycle": "Go to previous unread room (cycle)", - "undo_in_message_editor": "Undo in message editor", - "redo_in_message_editor": "Redo in message editor", - "bold": "Bold", - "italic": "Italic", - "underline": "Underline", - "keyboard_shortcuts": "Keyboard Shortcuts", - "close_keyboard_shortcuts": "Close keyboard shortcuts", - "messages": "Messages", - "navigation": "Navigation" - }, - "Persona": { - "limited_compatibility_with_pluralkit_like_functions": "Limited Compatibility with PluralKit-like functions", - "enable_pk_commands": "Enable PK commands", - "if_enabled_it_will_enable_a_few_pk_style_commands_currently_verry_limited": "If enabled, it will enable a few pk style commands, currently verry limited", - "disable_pk_commands": "disable pk; commands", - "enable_pks_commands": "enable pk; commands", - "enable_shorthands": "Enable Shorthands", - "if_enabled_you_can_use_shorthands_to_use_a_persona_for_one_message_only_eg": "If enabled, you can use shorthands to use a Persona for one message only (eg. '✨:test')", - "disable_checking_typed_messages_for_shorthands": "disable checking typed messages for shorthands", - "enable_checking_typed_messages_for_shorthands": "enable checking typed messages for shorthands" - } - }, - "General": { - "continue": "Continue", - "passphrase": "Passphrase", - "optional": "optional", - "unexpected_error": "Unexpected Error!", - "recovery_key": "Recovery Key", - "download": "Download", - "copy": "Copy", - "hide": "Hide", - "show": "Show", - "recovery_passphrase": "Recovery Passphrase", - "reset": "Reset", - "close": "Close", - "accept": "Accept", - "okay": "Okay", - "retry": "Retry", - "user": "User", - "delete": "Delete", - "dismiss": "Dismiss", - "nickname": "Nickname", - "save": "Save", - "clear": "Clear", - "edit_nickname": "Edit Nickname", - "set_nickname": "Set Nickname", - "cancel": "Cancel", - "upload": "Upload", - "upload_area": "Upload area", - "display_name": "Display name", - "pronouns": "Pronouns", - "unknown": "Unknown", - "edit": "Edit" - }, - "RoomView": { - "failed_to_load_history": "Failed to load history.", - "failed_to_load_messages": "Failed to load messages.", - "jump_to_unread": "Jump to Unread", - "mark_as_read": "Mark as Read", - "new_messages": "New Messages", - "jump_to_latest": "Jump to Latest", - "call_started_by": "Call started by", - "start_voice_call": "Start Voice Call", - "typing": { - "are_typing": " are typing...", - "typing_sep_word": " and " - }, - "is_typing": "is typing...", - "close_threads": "Close threads", - "search_threads": "Search threads...", - "clear_search": "Clear search", - "Threads": { - "no_threads_match_your_search": "No threads match your search.", - "no_threads_yet": "No threads yet.", - "jump": "Jump", - "threads": "Threads", - "thread": "Thread", - "no_replies_yet_start_the_thread_below": "No replies yet. Start the thread below!" - }, - "join_new_room": "Join New Room", - "this_room_has_been_replaced_and_is_no_longer_active": "This room has been replaced and is no longer active.", - "failed_to_join_replacement_room": "Failed to join replacement room!", - "open_new_room": "Open New Room", - "scroll_to_top": "Scroll to Top", - "Message": { - "pin_message": "Pin Message", - "unpin_message": "Unpin Message", - "forwarded_private_message": "Forwarded private message", - "forwarded_from_earlier_in_this_room": "Forwarded from earlier in this room", - "forwarded_from_another_room": "Forwarded from another room", - "jump_to_original": "jump to original", - "failed_to_send": "Failed to send.", - "only_you_can_see_this": "Only you can see this.", - "edit_message": "Edit Message", - "Editor": { - "edit_message": "Edit message..." - } - }, - "Reactions": { - "reacted_with": "Reacted with" - } - }, - "RoomInput": { - "recording_duration": "Recording duration:", - "EmojiBoard": { - "no_results_found": "No Results found", - "search_results": "Search Results", - "personal_pack": "Personal Pack", - "unknown_pack": "Unknown Pack" - } - }, - "RoomCreate": { - "versions": "Versions", - "founders": "Founders", - "special_privileged_users_can_be_assigned_during_creation_these_users_have_e": "Special privileged users can be assigned during creation. These users have elevated control and can only be modified during a upgrade.", - "no_suggestions": "No Suggestions", - "please_provide_the_user_id_and_hit_enter": "Please provide the user ID and hit Enter.", - "only_member_of_parent_space_can_join": "Only member of parent space can join.", - "private": "Private", - "only_people_with_invite_can_join": "Only people with invite can join.", - "anyone_with_the_address_can_join": "Anyone with the address can join.", - "public": "Public", - "address_optional": "Address (Optional)", - "pick_an_unique_address_to_make_it_discoverable": "Pick an unique address to make it discoverable.", - "this_address_is_already_taken_please_select_a_different_one": "This address is already taken. Please select a different one.", - "voice_room": "Voice Room", - "live_audio_and_video_conversations": "- Live audio and video conversations.", - "chat_room": "Chat Room", - "messages_photos_and_videos": "- Messages, photos, and videos." - }, - "Room": { - "DirectInvite": { - "direct_message_invite_prompt": "This is a Direct Message room, intended for a conversation between two persons. Would you like to convert it into a group chat before continuing?", - "failed_to_convert_direct_message_to_room": "Failed to convert direct message to room!", - "convert_to_group_chat_and_invite": "Convert to Group Chat and Invite", - "invite_to_direct_message_anyway": "Invite to Direct Message anyway", - "invite_another_member": "Invite another Member" - } - }, - "DevTools": { - "json_content": "JSON Content", - "account_data": "Account Data", - "developer_tools": "Developer Tools" - } -} diff --git a/public/locales/en/general.json b/public/locales/en/general.json new file mode 100644 index 0000000000..85d4cca31e --- /dev/null +++ b/public/locales/en/general.json @@ -0,0 +1,169 @@ +{ + "continue": "Continue", + "passphrase": "Passphrase", + "optional": "optional", + "unexpected_error": "Unexpected Error!", + "recovery_key": "Recovery Key", + "download": "Download", + "copy": "Copy", + "hide": "Hide", + "show": "Show", + "recovery_passphrase": "Recovery Passphrase", + "reset": "Reset", + "close": "Close", + "accept": "Accept", + "okay": "Okay", + "retry": "Retry", + "user": "User", + "delete": "Delete", + "dismiss": "Dismiss", + "nickname": "Nickname", + "save": "Save", + "clear": "Clear", + "edit_nickname": "Edit Nickname", + "set_nickname": "Set Nickname", + "cancel": "Cancel", + "upload": "Upload", + "upload_area": "Upload area", + "display_name": "Display name", + "pronouns": "Pronouns", + "unknown": "Unknown", + "edit": "Edit", + "Organisms": { + "RoomCommon": { + "changed_room_name": " changed room name" + } + }, + "Settings": { + "Cosmetics": { + "light_and_dark": "Light & dark", + "light_only": "Light only", + "dark_only": "Dark only", + "off": "Off", + "jumbo_emoji": "Jumbo Emoji", + "jumbo_emoji_size": "Jumbo Emoji Size", + "adjust_the_size_of_emojis_sent_without_text": "Adjust the size of emojis sent without text.", + "privacy_and_security": "Privacy & Security", + "blur_media": "Blur Media", + "blurs_images_and_videos_in_the_timeline": "Blurs images and videos in the timeline.", + "blur_avatars": "Blur Avatars", + "blurs_user_profile_pictures_and_room_icons": "Blurs user profile pictures and room icons.", + "blur_emotes": "Blur Emotes", + "blurs_emoticons_within_messages": "Blurs emoticons within messages.", + "identity": "Identity", + "colorful_names": "Colorful Names", + "assign_unique_colors_to_users_based_on_their_id_does_not_override_room_spac": "Assign unique colors to users based on their ID. Does not override room/space custom colors. Will override default role colors.", + "show_pronoun_pills": "Show Pronoun Pills", + "display_user_pronouns_in_the_message_timeline": "Display user pronouns in the message timeline.", + "max_pronoun_pills": "Max Pronoun Pills", + "maximum_number_of_pronoun_pills": "Maximum number of pronoun pills shown per user in the timeline. Additional pronouns appear behind the ... pill.", + "max_pronoun_pill_length": "Max Pronoun Pill Length", + "maximum_pronoun_pill_length": "Maximum characters shown in each pronoun pill before truncation.", + "pronoun_pills_for_all": "Pronoun Pills for All", + "attempts_to_convert_pronouns_in_names_into_pills_e_g_they_them_or_it_its_tu": "Attempts to convert pronouns in names into pills (e.g. [they/them] or (it/its) turns into a pill).", + "render_custom_profile_cards": "Render Custom Profile Cards", + "choose_whose_profile_card_colors_to_show_everyone_with_a_scheme_only_light": "Choose whose profile card colors to show: everyone with a scheme, only light or dark schemes, or hide them.", + "render_global_username_colors": "Render Global Username Colors", + "display_the_username_colors_anyone_can_set_in_their_account_settings": "Display the username colors anyone can set in their account settings.", + "render_space_room_username_colors": "Render Space/Room Username Colors", + "display_the_username_colors_that_can_be_set_with_color": "Display the username colors that can be set with /color.", + "render_space_room_fonts": "Render Space/Room Fonts", + "display_the_username_fonts_that_can_be_set_with_font": "Display the username fonts that can be set with /font.", + "consistent_icon_style": "Consistent Icon Style", + "harmonize_icon_appearance_with_background_fill": "Harmonize icon appearance with background fill", + "extra_small": "Extra Small", + "none_same_size_as_text": "None (Same size as text)", + "small": "Small", + "normal": "Normal", + "large": "Large", + "extra_large": "Extra Large", + "language_specific_pronouns": "Language Specific Pronouns", + "show_pronouns_only_in_selected_language": "Show pronouns only in selected language", + "if_enabled_pronouns_are_only_shown_when_they_match_your_selected_language_t": "If enabled, pronouns are only shown when they match your selected language. This helps if your contacts set pronouns in different languages. It doesn't affect how your pronouns are shared with others.", + "selected_language_for_pronouns": "Selected language for pronouns", + "the_language_to_show_pronouns_for_when_the_above_setting_is_enabled": "The language to show pronouns for when the above setting is enabled.", + "language_code_e_g_en_de_en_de": "Language code (e.g. 'en', 'de', 'en,de')" + }, + "General": { + "formatting": "Formatting", + "month": "Month", + "day_of_the_week": "Day of the Week", + "day_of_the_week_sunday_0": "Day of the week (Sunday = 0)", + "two_letter_day_name": "Two-letter day name", + "short_day_name": "Short day name", + "full_day_name": "Full day name", + "day_of_the_month": "Day of the Month", + "full_month_name": "Full month name", + "short_month_name": "Short month name", + "the_month": "The month", + "two_digit_month": "Two-digit month", + "four_digit_year": "Four-digit year", + "two_digit_year": "Two-digit year" + } + }, + "RoomView": { + "failed_to_load_history": "Failed to load history.", + "failed_to_load_messages": "Failed to load messages.", + "jump_to_unread": "Jump to Unread", + "mark_as_read": "Mark as Read", + "new_messages": "New Messages", + "jump_to_latest": "Jump to Latest", + "call_started_by": "Call started by", + "start_voice_call": "Start Voice Call", + "typing": { + "are_typing": " are typing...", + "typing_sep_word": " and " + }, + "is_typing": "is typing...", + "close_threads": "Close threads", + "search_threads": "Search threads...", + "clear_search": "Clear search", + "Threads": { + "no_threads_match_your_search": "No threads match your search.", + "no_threads_yet": "No threads yet.", + "jump": "Jump", + "threads": "Threads", + "thread": "Thread", + "no_replies_yet_start_the_thread_below": "No replies yet. Start the thread below!" + }, + "join_new_room": "Join New Room", + "this_room_has_been_replaced_and_is_no_longer_active": "This room has been replaced and is no longer active.", + "failed_to_join_replacement_room": "Failed to join replacement room!", + "open_new_room": "Open New Room", + "scroll_to_top": "Scroll to Top", + "Message": { + "pin_message": "Pin Message", + "unpin_message": "Unpin Message", + "forwarded_private_message": "Forwarded private message", + "forwarded_from_earlier_in_this_room": "Forwarded from earlier in this room", + "forwarded_from_another_room": "Forwarded from another room", + "jump_to_original": "jump to original", + "failed_to_send": "Failed to send.", + "only_you_can_see_this": "Only you can see this.", + "edit_message": "Edit Message", + "Editor": { + "edit_message": "Edit message..." + } + }, + "Reactions": { + "reacted_with": "Reacted with" + } + }, + "RoomInput": { + "recording_duration": "Recording duration:", + "EmojiBoard": { + "no_results_found": "No Results found", + "search_results": "Search Results", + "personal_pack": "Personal Pack", + "unknown_pack": "Unknown Pack" + } + }, + "Room": { + "DirectInvite": {} + }, + "DevTools": { + "json_content": "JSON Content", + "account_data": "Account Data", + "developer_tools": "Developer Tools" + } +} diff --git a/public/locales/en/room/create.json b/public/locales/en/room/create.json new file mode 100644 index 0000000000..ba437d4bf5 --- /dev/null +++ b/public/locales/en/room/create.json @@ -0,0 +1,19 @@ +{ + "versions": "Versions", + "founders": "Founders", + "special_privileged_users_can_be_assigned_during_creation_these_users_have_e": "Special privileged users can be assigned during creation. These users have elevated control and can only be modified during a upgrade.", + "no_suggestions": "No Suggestions", + "please_provide_the_user_id_and_hit_enter": "Please provide the user ID and hit Enter.", + "only_member_of_parent_space_can_join": "Only member of parent space can join.", + "private": "Private", + "only_people_with_invite_can_join": "Only people with invite can join.", + "anyone_with_the_address_can_join": "Anyone with the address can join.", + "public": "Public", + "address_optional": "Address (Optional)", + "pick_an_unique_address_to_make_it_discoverable": "Pick an unique address to make it discoverable.", + "this_address_is_already_taken_please_select_a_different_one": "This address is already taken. Please select a different one.", + "voice_room": "Voice Room", + "live_audio_and_video_conversations": "- Live audio and video conversations.", + "chat_room": "Chat Room", + "messages_photos_and_videos": "- Messages, photos, and videos." +} diff --git a/public/locales/en/room/direct/invite.json b/public/locales/en/room/direct/invite.json new file mode 100644 index 0000000000..fc40e8983a --- /dev/null +++ b/public/locales/en/room/direct/invite.json @@ -0,0 +1,7 @@ +{ + "direct_message_invite_prompt": "This is a Direct Message room, intended for a conversation between two persons. Would you like to convert it into a group chat before continuing?", + "failed_to_convert_direct_message_to_room": "Failed to convert direct message to room!", + "convert_to_group_chat_and_invite": "Convert to Group Chat and Invite", + "invite_to_direct_message_anyway": "Invite to Direct Message anyway", + "invite_another_member": "Invite another Member" +} diff --git a/public/locales/en/settings/device_verification.json b/public/locales/en/settings/device_verification.json new file mode 100644 index 0000000000..2a69592bac --- /dev/null +++ b/public/locales/en/settings/device_verification.json @@ -0,0 +1,28 @@ +{ + "error_uia_action_without_data": "Unexpected Error! UIA action is perform without data.", + "authentication_failed_failed_to_setup_device_verification": "Authentication failed! Failed to setup device verification.", + "unexpected_error_crypto_module_not_found": "Unexpected Error! Crypto module not found!", + "unexpected_error_failed_to_create_recovery_key": "Unexpected Error! Failed to create recovery key.", + "generate_a_recovery_key": "Generate a recovery key for verifying identity if you do not have access to other devices. Additionally, setup a passphrase as a memorable alternative", + "optional_passphrase": "Passphrase (optional)", + "authentication_steps_to_perform_this_action_are_not_supported_by_client": "Authentication steps to perform this action are not supported by client.", + "store_the_recovery_key_in_a_safe_place_for_future_use_as_you_will_need_it_t": "Store the Recovery Key in a safe place for future use, as you will need it to verify your identity if you do not have access to other devices.", + "setup_device_verification": "Setup Device Verification", + "reset_device_verification": "Reset Device Verification", + "resetting_device_verification_is_permanent": "Resetting device verification is permanent.", + "anyone_you_have_verified_with_will_see_security_alerts_and_your_encryption": "Anyone you have verified with will see security alerts and your encryption backup will be lost. You almost certainly do not want to do this, unless you have lost", + "and_every_device_you_can_verify_from": "and every device you can verify from.", + "please_accept_the_request_from_other_device": "Please accept the request from other device.", + "click_accept_to_start_the_verification_process": "Click accept to start the verification process.", + "waiting_for_request_to_be_accepted": "Waiting for request to be accepted...", + "confirm_the_emoji_below_are_displayed_on_both_devices_in_the_same_order": "Confirm the emoji below are displayed on both devices, in the same order:", + "they_match": "They Match", + "do_not_match": "Do not Match", + "starting_verification_using_emoji_comparison": "Starting verification using emoji comparison...", + "your_device_is_verified": "Your device is verified.", + "verification_has_been_canceled": "Verification has been canceled.", + "device_verification": "Device Verification", + "unexpected_error_verification_is_started_but_verifier_is_missing": "Unexpected Error! Verification is started but verifier is missing.", + "verification_request_has_been_accepted": "Verification request has been accepted.", + "waiting_for_the_response_from_other_device": "Waiting for the response from other device..." +} diff --git a/public/locales/en/settings/experimental.json b/public/locales/en/settings/experimental.json new file mode 100644 index 0000000000..1a580e6551 --- /dev/null +++ b/public/locales/en/settings/experimental.json @@ -0,0 +1,22 @@ +{ + "save_bandwidth_for_sticker_and_emoji_images": "Save Bandwidth for Sticker and Emoji Images", + "enable_bandwidth_saving_for_stickers_and_emojis": "Enable bandwidth saving for stickers and emojis", + "if_enabled_sticker_and_emoji_images_will_be_optimized_to_save_bandwidth_thi": "If enabled, sticker and emoji images will be optimized to save bandwidth. This helps reduce data usage when viewing these images. But will increase server computation load.", + "personas_per_message_profiles": "Personas (Per-Message Profiles)", + "show_personas_tab": "Show Personas Tab", + "enables_the_personas_tab_in_the_settings_menu_for_per_message_profiles": "Enables the personas tab in the settings menu for per-message profiles", + "experimental": "Experimental", + "the_features_listed_below_may_be_unstable_or_incomplete": "The features listed below may be unstable or incomplete,", + "use_at_your_own_risk": "use at your own risk", + "please_report_any_new_issues_potentially_caused_by_these_features": "Please report any new issues potentially caused by these features!", + "enable_sharing_of_encrypted_history": "Enable Sharing of Encrypted History", + "enable_the_sharehistory_command": "Enable the /sharehistory command", + "if_enabled_this_command_will_allow_users_to_share_encrypted_history_with_ot": "If enabled, this command will allow users to share encrypted history with other newly joined users, as per MSC4268.", + "disable_sharehistory_command": "Disable /sharehistory command", + "enable_sharehistory_command": "Enable /sharehistory command", + "enable_media_galleries_support": "Enable Media Galleries Support", + "enable_media_galleries_title": "Enable Media Galleries", + "enable_media_galleries_description": "If enabled, multiple attachments will be sent in one message, as per MSC4274. Incompatible with clients that don't implement it.", + "disable_media_galleries": "Disable Media Galleries", + "enable_media_galleries": "Enable Media Gallleries" +} diff --git a/public/locales/en/settings/keyboard_shortcuts.json b/public/locales/en/settings/keyboard_shortcuts.json new file mode 100644 index 0000000000..1cd7cfa7eb --- /dev/null +++ b/public/locales/en/settings/keyboard_shortcuts.json @@ -0,0 +1,18 @@ +{ + "search_for_messages": "Search for messages", + "jump_to_the_highest_priority_unread_room": "Jump to the highest-priority unread room", + "go_to_next_unread_room_cycle": "Go to next unread room (cycle)", + "go_to_previous_unread_room_cycle": "Go to previous unread room (cycle)", + "undo_in_message_editor": "Undo in message editor", + "redo_in_message_editor": "Redo in message editor", + "seach_and_go_to_room": "Search and go to Room", + "open_sticker_picker": "Open Sticker Picker", + "bold": "Bold", + "italic": "Italic", + "underline": "Underline", + "keyboard_shortcuts": "Keyboard Shortcuts", + "close_keyboard_shortcuts": "Close keyboard shortcuts", + "general": "General", + "navigation": "Navigation", + "messages": "Messages" +} diff --git a/public/locales/en/settings/persona.json b/public/locales/en/settings/persona.json new file mode 100644 index 0000000000..36d53633e7 --- /dev/null +++ b/public/locales/en/settings/persona.json @@ -0,0 +1,21 @@ +{ + "limited_compatibility_with_pluralkit_like_functions": "Limited Compatibility with PluralKit-like functions", + "enable_pk_commands": "Enable PK commands", + "if_enabled_it_will_enable_a_few_pk_style_commands_currently_verry_limited": "If enabled, it will enable a few pk style commands, currently verry limited", + "disable_pk_commands": "disable pk; commands", + "enable_pks_commands": "enable pk; commands", + "enable_shorthands": "Enable Shorthands", + "if_enabled_you_can_use_shorthands_to_use_a_persona_for_one_message_only_eg": "If enabled, you can use shorthands to use a Persona for one message only (eg. '✨:test')", + "disable_checking_typed_messages_for_shorthands": "disable checking typed messages for shorthands", + "enable_checking_typed_messages_for_shorthands": "enable checking typed messages for shorthands", + "profile_id": "Profile ID:", + "pronouns": "Pronouns:", + "reset_pronouns": "Reset Pronouns", + "display_name": "Display Name:", + "delete_profile": "Delete profile {{profileId}}", + "save_profile": "Save profile changes for {{profileId}}", + "save_profile_button_area": "Save button area for {{profileId}}", + "pronouns_for": "Pronouns for {{profileId}}", + "avatar_for": "Avatar for {{profileId}}", + "display_name_for": "Display name for {{profileId}}" +} diff --git a/public/locales/en/settings/profile.json b/public/locales/en/settings/profile.json new file mode 100644 index 0000000000..1a4a955f52 --- /dev/null +++ b/public/locales/en/settings/profile.json @@ -0,0 +1,8 @@ +{ + "avatar_and_upload": "Avatar and upload", + "profile_avatar": "Profile avatar", + "avatar_fallback": "Avatar fallback", + "upload_avatar_image": "Upload avatar image", + "display_name_input": "Display name input", + "reset_display_name": "Reset display name" +} diff --git a/public/locales/ro.json b/public/locales/ro.json deleted file mode 100644 index 081ec24f56..0000000000 --- a/public/locales/ro.json +++ /dev/null @@ -1,273 +0,0 @@ -{ - "Organisms": { - "RoomCommon": { - "changed_room_name": " a schimbat numele camerei" - } - }, - "Settings": { - "device_verification": { - "error_uia_action_without_data": "Eroare neasΜ¦teptataΜ†! Actiunea UIA este facutaΜ† faraΜ† date.", - "authentication_failed_failed_to_setup_device_verification": "Autentificare esΜ¦uataΜ†! Verificarea dispozitivului a esΜ¦uat.", - "unexpected_error_crypto_module_not_found": "Eroare neasΜ¦teptataΜ†! Modulul Crypto nu poate fi accessat!", - "unexpected_error_failed_to_create_recovery_key": "Eroare neasΜ¦teptataΜ†! Incercarea de a crea o cheie de rezervaΜ† a esΜ¦uat.", - "generate_a_recovery_key": "GeneratΜ¦i o cheie de recuperare pentru a iΜ‚tΜ¦i verifica identitatea in cazul in care nu ai access la alte dispozitive. AditΜ¦ional, seteazaΜ† o parolaΜ† ca o alternativaΜ† mai usΜ¦oaraΜ† de amintit.", - "optional_passphrase": "ParolaΜ† (optionalaΜ†)", - "authentication_steps_to_perform_this_action_are_not_supported_by_client": "PasΜ¦i pentru autentificare nu sunt acceptatΜ¦i de aceastaΜ† aplicatΜ¦ie.", - "store_the_recovery_key_in_a_safe_place_for_future_use_as_you_will_need_it_t": "SalveazaΜ† cheia de recuperare intr-un loc sigur pentru a fi accesataΜ† in viitor. Vei avea nevoie de ea sa iΜ‚tΜ¦i verifici identitatea in cazul in care nu ai acces la alte dispozitive.", - "setup_device_verification": "ConfigureazaΜ† verificare dispozitivelor.", - "reset_device_verification": "Reseteaza Identitea de verificare a dispozitivelor", - "resetting_device_verification_is_permanent": "Resetarea verificaΜ†rii dispozitivelor este permanentaΜ†!", - "anyone_you_have_verified_with_will_see_security_alerts_and_your_encryption": "ToataΜ† lumea cu care ai vorbit va primii o alertaΜ† de securitate si encriptia de rezervaΜ† va fi pierdutaΜ†. Aproape sigur nu vrei saΜ† faci asta, singura situatΜ¦ie iΜ‚n care ai vrea saΜ† faci asta este daca ai pierdut", - "and_every_device_you_can_verify_from": "sΜ¦i orice alt dispozitiv cu care ai putea verifica.", - "please_accept_the_request_from_other_device": "AcceptatΜ¦i cererea dintr-un alt dispozitiv.", - "click_accept_to_start_the_verification_process": "Apasa pe 'AcceptaΜ†' pentru a iΜ‚ncepe procesul de verificare.", - "waiting_for_request_to_be_accepted": "AsteptaΜ†m ca cererea saΜ† fie acceptataΜ†...", - "confirm_the_emoji_below_are_displayed_on_both_devices_in_the_same_order": "ConfirmaΜ† ca emoticoanele prezentate ulterior sunt aceleasΜ¦i pe ambele dispozitive, sΜ¦i iΜ‚n aceasΜ¦i ordine:", - "they_match": "Se potrivesc", - "do_not_match": "Sunt diferite", - "starting_verification_using_emoji_comparison": "IΜ‚ncepe verificare prin comparare de emoticoane...", - "your_device_is_verified": "Dispozitivul acesta e verificat.", - "verification_has_been_canceled": "Verificarea a forst opritaΜ†.", - "device_verification": "Verificare dispozitiv", - "unexpected_error_verification_is_started_but_verifier_is_missing": "Eroare neasΜ¦teptataΜ†! Verificarea a iΜ‚nceput dar verificatorul a dispaΜ†rut.", - "verification_request_has_been_accepted": "Cererea de verificare a fost acceptataΜ†.", - "waiting_for_the_response_from_other_device": "AsΜ¦teptaΜ†m pentru un raΜ†spuns de la celaΜ†lalt dispozitiv..." - }, - "PerMessageProfiles": { - "profile_id": "ID profil:", - "display_name": "Nume AfisΜ¦at:" - }, - "Profile": { - "avatar_and_upload": "Imagine de profil si iΜ‚ncaΜ†rcare", - "profile_avatar": "Imagine de profil", - "avatar_fallback": "Imagine de rezervaΜ†", - "upload_avatar_image": "IΜ‚ncarcaΜ† imagine de profil", - "display_name_input": "AfisΜ¦ introducere nume de profil", - "reset_display_name": "ReseteazaΜ† numele afisΜ¦at" - }, - "Cosmetics": { - "light_and_dark": "Luminos & IΜ‚ntunecat", - "light_only": "Doar luminos", - "dark_only": "Doar iΜ‚ntunecat", - "off": "NiciodataΜ†", - "jumbo_emoji": "Emojiuri Jumbo", - "jumbo_emoji_size": "MaΜ†rime Emojiuri Jumbo", - "adjust_the_size_of_emojis_sent_without_text": "SchimbaΜ† maΜ†rimea emojiurilor trimise faΜ‚raΜ† text.", - "privacy_and_security": "ConfidenΘ›ialitate Θ™i Securitate", - "blur_media": "EstompazaΜ† media.", - "blurs_images_and_videos_in_the_timeline": "EstompeazaΜ† imaginile si videourile din mesaje.", - "blur_avatars": "EstompeazaΜ† avatar-uri", - "blurs_user_profile_pictures_and_room_icons": "EstompeazaΜ† imaginile de profil si iconitΜ¦ele camerelor.", - "blur_emotes": "EstompeazaΜ† emoticoane", - "blurs_emoticons_within_messages": "EstompeazaΜ† emoticoanele din mesaje.", - "identity": "Identitate", - "colorful_names": "Nume Colorate Aleatoriu", - "assign_unique_colors_to_users_based_on_their_id_does_not_override_room_spac": "Atribuie culori unici utilizatorilor pe baza ID-ului lor. AceastaΜ† setare nu va inlocuii culorile atribuite de cameraΜ†/spatΜ¦iu, dar va inlocuii culoarea rolului de bazaΜ†", - "show_pronoun_pills": "ArataΜ† pilule de pronume", - "display_user_pronouns_in_the_message_timeline": "Arata pronumele utilizatorilor in mesaje", - "max_pronoun_pills": "NumaΜ†r maxim de pilule de pronume", - "maximum_number_of_pronoun_pills": "NumaΜ†rul maxim de pilule de pronume prezente de persoanaΜ† in conversatie. Alte pronume apar dupaΜ† pilula intitulataΜ† '...'", - "max_pronoun_pill_length": "Lungimea maximaΜ† a pronumelor de profil", - "maximum_pronoun_pill_length": "NumaΜ†rul maxim de litere in fiecare pronume iΜ‚nainte de a fi trunchiataΜ†.", - "pronoun_pills_for_all": "Pilule de pronume pentru toataΜ† lumea", - "attempts_to_convert_pronouns_in_names_into_pills_e_g_they_them_or_it_its_tu": "IΜ‚ncearcaΜ† sa transformi pronumele din nume in pilule (ex. [el/lui] sau [ea/iei] este transformat in pilulaΜ†).", - "render_custom_profile_cards": "RedaΜ† carduri de profil personalizate.", - "choose_whose_profile_card_colors_to_show_everyone_with_a_scheme_only_light": "Alege caΜ‚nd sa se redea cardul de profil: pentru toataΜ† lumea, doar teme iΜ‚ntunecate, doar teme luminoase, sau nu le reda deloc.", - "render_global_username_colors": "RedaΜ† culorile generale numelor persoanelor", - "display_the_username_colors_anyone_can_set_in_their_account_settings": "RedaΜ† culorile numelor tuturor persoanelor care au fost setate de acestea.", - "render_space_room_username_colors": "RedaΜ† culorile numelor setate de CameraΜ†/SpatΜ¦iu.", - "display_the_username_colors_that_can_be_set_with_color": "RedaΜ† culorile numelor care pot fi setate cu /color.", - "render_space_room_fonts": "RedaΜ† fonturile Camerei/SpatΜ¦iului.", - "display_the_username_fonts_that_can_be_set_with_font": "RedaΜ† fontul numelor care pot fi setate cu /font.", - "consistent_icon_style": "Stil al icoanelor consistent.", - "harmonize_icon_appearance_with_background_fill": "ArmonizeazaΜ† forma icoanelor cu o culoare de fundal.", - "extra_small": "Foarte mic", - "none_same_size_as_text": "BazaΜ† (aceasΜ¦i maΜ†rime ca textul)", - "small": "Mic", - "normal": "Normal", - "large": "Mari", - "extra_large": "Foarte mari", - "language_specific_pronouns": "Pronume specifice limbii", - "show_pronouns_only_in_selected_language": "RedaΜ† doar pronumele in limba selectataΜ†", - "if_enabled_pronouns_are_only_shown_when_they_match_your_selected_language_t": "DacaΜ† activat, doar pronumele in limba selectataΜ† de tine vor apaΜ†rea. Asta ajuta dacaΜ† contactele tale au setata pronume in limbi diferite. Nu afecteaza cum pronumele setate de tine sunt iΜ‚mpaΜ†rtaΜ†sΜ¦ite.", - "selected_language_for_pronouns": "Limbi selectate pentru pronume.", - "the_language_to_show_pronouns_for_when_the_above_setting_is_enabled": "Limbile in care pronumele vor fi redate pentru setarea de asupra.", - "language_code_e_g_en_de_en_de": "Codul limbii (ex. 'ro', 'de', 'ro,de')" - }, - "save_bandwidth_for_sticker_and_emoji_images": "ReducetΜ¦i Consumul retΜ¦elei pentru stickere si emoticoane", - "enable_bandwidth_saving_for_stickers_and_emojis": "Porneste salvare de 'bandwidth' pentru stickere si emoticoane", - "if_enabled_sticker_and_emoji_images_will_be_optimized_to_save_bandwidth_thi": "DacaΜ† pornit, stickerele si emoticoanele vor fi optimizate pentru a consuma mai putΜ¦in internet. Asta ajutaΜ† consumul de date caΜ‚nd vaΜ†zaΜ†nd aceste imagini, dar cresΜ¦te costul de computare a server-ului.", - "personas_per_message_profiles": "PersonalitaΜ†tΜ¦i. (Profiluri per-mesaj)", - "show_personas_tab": "PrezintaΜ† meniul de PersonalitaΜ†tΜ¦i", - "enables_the_personas_tab_in_the_settings_menu_for_per_message_profiles": "Adauga meniul de personalitaΜ†tΜ¦i in bara de setaΜ†ri pentru profile specifice fiecaΜ†rui mesaj.", - "experimental": "Experimental", - "the_features_listed_below_may_be_unstable_or_incomplete": "OptΜ¦iunile listate ulterior pot fi instabile sau incomplete,", - "use_at_your_own_risk": "folosesΜ¦te la riscul vostru", - "please_report_any_new_issues_potentially_caused_by_these_features": "VaΜ† rugaΜ†m sa raportati orice probleme noi cauzate potentΜ¦ial de aceste optΜ¦iuni! Mersi!", - "enable_sharing_of_encrypted_history": "ActiveazaΜ† iΜ‚mpaΜ†rtaΜ†sΜ¦irea de Istorie CriptataΜ†.", - "enable_the_sharehistory_command": "ActiveazaΜ† comanda de /sharehistory", - "if_enabled_this_command_will_allow_users_to_share_encrypted_history_with_ot": "DacaΜ† activataΜ†, aceastaΜ† optiune va permite utilizatorilor saΜ† impaΜ†rtasΜ¦eascaΜ† istoria criptataΜ† cu altΜ¦i utilizatori noi, per MSC4268.", - "disable_sharehistory_command": "DezactiveazaΜ† comanda /sharehistory", - "enable_sharehistory_command": "ActiveazaΜ† comanda /sharehistory", - "General": { - "formatting": "Formatare", - "month": "Luna", - "day_of_the_week": "Ziua saΜ†ptaΜ†maΜ†nii", - "day_of_the_week_sunday_0": "Ziua saΜ†ptaΜ†maΜ†nii (DuminicaΜ† = 0)", - "two_letter_day_name": "Numele zilei cu 2 litere", - "short_day_name": "Numele zilei scurt", - "full_day_name": "Numele zilei iΜ‚ntreg", - "day_of_the_month": "Ziua din lunaΜ†", - "full_month_name": "Numele iΜ‚ntreg al lunii", - "short_month_name": "Numele scurt al lunii", - "the_month": "Luna", - "two_digit_month": "Luna cu 2 cifre", - "four_digit_year": "Anul cu 4 cifre", - "two_digit_year": "Anul cu 2 cifre" - }, - "KeyboardShortcuts": { - "jump_to_the_highest_priority_unread_room": "Dute la camera cu cea mai ridicata prioritate de necitire", - "go_to_next_unread_room_cycle": "Dute la urmaΜ†toarea camera necititaΜ† (urmeazaΜ† un ciclu)", - "go_to_previous_unread_room_cycle": "Go to previous unread room (urmeazaΜ† un ciclu)", - "undo_in_message_editor": "SΜ¦terge iΜ‚n editorul de mesaje", - "redo_in_message_editor": "Reface iΜ‚n editorul de mesaje", - "bold": "IΜ‚ngrosΜ¦at", - "italic": "Italic", - "underline": "Subliniat", - "navigation": "NavigatΜ¦ie" - }, - "Persona": { - "limited_compatibility_with_pluralkit_like_functions": "Compatibilitate limitataΜ† cu functii similare cu PluralKit", - "enable_pk_commands": "ActiveazaΜ† comenzi PK", - "if_enabled_it_will_enable_a_few_pk_style_commands_currently_verry_limited": "DacaΜ† activat, va permite folosirea de comenzi in stil PK, deocamdataΜ† foarte limitate", - "disable_pk_commands": "dezactiveazaΜ† comenzile pk;", - "enable_pks_commands": "activeazaΜ† comenzile pk;", - "enable_shorthands": "ActiveazaΜ† scurtaΜ†turi", - "if_enabled_you_can_use_shorthands_to_use_a_persona_for_one_message_only_eg": "DacaΜ† activat, vei putea folosii scuraturi pentru a folosi personalitaΜ†tΜ¦i pentru un mesaj (ex. '✨:test')", - "disable_checking_typed_messages_for_shorthands": "dezactiveazaΜ† verificarea mesajelor scrise pentru scurataturi", - "enable_checking_typed_messages_for_shorthands": "activeaza verificarea mesajelor scrise pentru scurataturi" - } - }, - "General": { - "continue": "ContinuaΜ†", - "passphrase": "ParolaΜ†", - "optional": "optΜ¦ional", - "unexpected_error": "Eroare NeasΜ¦teptataΜ†!", - "recovery_key": "Cheie de Recuperare", - "download": "DescarcaΜ†", - "copy": "CopiazaΜ†", - "hide": "Ascunde", - "show": "ArataΜ†", - "recovery_passphrase": "ParolaΜ† de recuperare", - "reset": "ReseteazaΜ†", - "close": "IΜ‚nchide", - "accept": "Accept", - "okay": "Okay", - "retry": "ReiΜ‚ncearcaΜ†", - "user": "utilizator", - "delete": "SΜ¦terge", - "dismiss": "Omite", - "nickname": "PoreclaΜ†", - "save": "SalveazaΜ†", - "clear": "CuraΜ†tΜ¦aΜ†", - "edit_nickname": "EditeazaΜ† PoreclaΜ†", - "set_nickname": "SeteazaΜ† PoreclaΜ†", - "cancel": "AnuleazaΜ†", - "upload": "IΜ‚ncarcaΜ†", - "upload_area": "ZonaΜ† iΜ‚ncaΜ†rcare", - "display_name": "Nume AfisΜ¦at", - "pronouns": "Pronume", - "unknown": "Necunoscut", - "edit": "EditeazaΜ†" - }, - "RoomView": { - "failed_to_load_history": "IΜ‚ncaΜ†rcarea istoriei a esΜ¦uat.", - "failed_to_load_messages": "IΜ‚ncaΜ†rcarea mesajelor a esΜ¦uat.", - "jump_to_unread": "Sari la necitit", - "mark_as_read": "NoteazaΜ† ca Citit", - "new_messages": "Mesaje Noi", - "jump_to_latest": "Sari la ultimele mesaje", - "call_started_by": "Apel iΜ‚nceput de", - "start_voice_call": "IΜ‚ncepe apel audio", - "typing": { - "are_typing": " scriu...", - "typing_sep_word": " sΜ¦i " - }, - "is_typing": "scrie...", - "close_threads": "IΜ‚nchide fire", - "search_threads": "CautaΜ† fire...", - "clear_search": "CuraΜ†tΜ¦aΜ† caΜ†utarea", - "Threads": { - "no_threads_match_your_search": "Nici un fir nu se potrivesΜ¦te cautaΜ†rii.", - "no_threads_yet": "IΜ‚nca nu sunt fire.", - "jump": "Sari", - "threads": "Fire", - "thread": "Fir", - "no_replies_yet_start_the_thread_below": "IΜ‚ncaΜ† nu are raΜ†spunsuri. IΜ‚ncepe un fir de mesaje!" - }, - "join_new_room": "AlaΜ†turaΜ†te unei camere noi", - "this_room_has_been_replaced_and_is_no_longer_active": "AceasaΜ† camera a fost schimbataΜ† sΜ¦i nu mai este activaΜ†.", - "failed_to_join_replacement_room": "IΜ‚ncercarea de a te alaΜ†tura camerei noi a esΜ¦uat!", - "open_new_room": "Deschide CameraΜ† nouaΜ†", - "scroll_to_top": "DeruleazaΜ† iΜ‚n Sus", - "Message": { - "pin_message": "FixeazaΜ† Mesajul", - "unpin_message": "Scoata fixarea Mesajului", - "forwarded_private_message": "Mesaj privat redirectΜ¦ionat", - "forwarded_from_earlier_in_this_room": "RedirectΜ¦ionat dintr-un mesaj mai vechi din aceastaΜ† cameraΜ†", - "forwarded_from_another_room": "RedirectΜ¦ionat din altaΜ† cameraΜ†", - "jump_to_original": "dute la original", - "failed_to_send": "Trimitere esΜ¦uataΜ†.", - "only_you_can_see_this": "Doar tu potΜ¦i vedea asta.", - "edit_message": "EditeazaΜ† mesajul", - "Editor": { - "edit_message": "EditeazaΜ† mesajul..." - } - }, - "Reactions": { - "reacted_with": "ReactΜ¦ionat cu" - } - }, - "RoomInput": { - "recording_duration": "Lungime iΜ‚nregistrare:", - "EmojiBoard": { - "no_results_found": "Nu un rezultat", - "search_results": "Rezultate caΜ†utare", - "personal_pack": "Pachet Personal", - "unknown_pack": "Pachet Necunoscut" - } - }, - "RoomCreate": { - "versions": "Versiuni", - "founders": "Fondatori", - "special_privileged_users_can_be_assigned_during_creation_these_users_have_e": "In durata creaΜ†ri, anumite persoane pot fi privilegiate. Aceste persoana au un control ridicat si pot fi modificate doar cu o modernizare a camerei.", - "no_suggestions": "Nici o sugestie", - "please_provide_the_user_id_and_hit_enter": "Oferiti ID-ul de utilizator si apaΜ†satΜ¦i Enter.", - "only_member_of_parent_space_can_join": "Doar membrii a spatΜ¦iului paΜ†rinte se pot alaΜ†tura.", - "private": "PrivataΜ†", - "only_people_with_invite_can_join": "Doar persoanele cu o invitatΜ¦ie se pot alaΜ†tura.", - "anyone_with_the_address_can_join": "Oricine are adresa se poate alaΜ†tura.", - "public": "PublicaΜ†", - "address_optional": "AdresaΜ† (OptΜ¦ionalaΜ†)", - "pick_an_unique_address_to_make_it_discoverable": "Alege o adresaΜ† unicaΜ† saΜ† poataΜ† fi comunitatea descoperitaΜ†.", - "this_address_is_already_taken_please_select_a_different_one": "AceastaΜ† adresaΜ† este deja luataΜ†. SelectatΜ¦i una diferitaΜ†.", - "voice_room": "CameraΜ† vocalaΜ†", - "live_audio_and_video_conversations": "; conversatΜ¦ii audio-video live.", - "chat_room": "CameraΜ† discutΜ¦ii", - "messages_photos_and_videos": "; Mesaje, fotografii, and videoclipuri." - }, - "Room": { - "DirectInvite": { - "direct_message_invite_prompt": "Aceasa este o conversatΜ¦ie directaΜ†, menitaΜ† pentru discutΜ¦ii iΜ‚ntre douaΜ† persoane, DoritΜ¦i sa o tranformatΜ¦i intr-un group de conversatΜ¦ie iΜ‚nainte de a continua?", - "failed_to_convert_direct_message_to_room": "Transformarea convesatΜ¦iei directe in conversatΜ¦ie de grup a esΜ¦uat!", - "convert_to_group_chat_and_invite": "TransformaΜ† in grup sΜ¦i invitaΜ†", - "invite_to_direct_message_anyway": "InvitaΜ† la mesaje directe oricum", - "invite_another_member": "InvitaΜ† altaΜ† persoana" - } - }, - "DevTools": { - "json_content": "ContΜ¦inut JSON", - "account_data": "Datele contului", - "developer_tools": "Unelete Dezvoltatori" - } -} diff --git a/public/locales/ro/general.json b/public/locales/ro/general.json new file mode 100644 index 0000000000..d4d6fbca93 --- /dev/null +++ b/public/locales/ro/general.json @@ -0,0 +1,166 @@ +{ + "continue": "ContinuaΜ†", + "passphrase": "ParolaΜ†", + "optional": "optΜ¦ional", + "unexpected_error": "Eroare NeasΜ¦teptataΜ†!", + "recovery_key": "Cheie de Recuperare", + "download": "DescarcaΜ†", + "copy": "CopiazaΜ†", + "hide": "Ascunde", + "show": "ArataΜ†", + "recovery_passphrase": "ParolaΜ† de recuperare", + "reset": "ReseteazaΜ†", + "close": "IΜ‚nchide", + "accept": "Accept", + "okay": "Okay", + "retry": "ReiΜ‚ncearcaΜ†", + "user": "utilizator", + "delete": "SΜ¦terge", + "dismiss": "Omite", + "nickname": "PoreclaΜ†", + "save": "SalveazaΜ†", + "clear": "CuraΜ†tΜ¦aΜ†", + "edit_nickname": "EditeazaΜ† PoreclaΜ†", + "set_nickname": "SeteazaΜ† PoreclaΜ†", + "cancel": "AnuleazaΜ†", + "upload": "IΜ‚ncarcaΜ†", + "upload_area": "ZonaΜ† iΜ‚ncaΜ†rcare", + "display_name": "Nume AfisΜ¦at", + "pronouns": "Pronume", + "unknown": "Necunoscut", + "edit": "EditeazaΜ†", + "Organisms": { + "RoomCommon": { + "changed_room_name": " a schimbat numele camerei" + } + }, + "Settings": { + "Cosmetics": { + "light_and_dark": "Luminos & IΜ‚ntunecat", + "light_only": "Doar luminos", + "dark_only": "Doar iΜ‚ntunecat", + "off": "NiciodataΜ†", + "jumbo_emoji": "Emojiuri Jumbo", + "jumbo_emoji_size": "MaΜ†rime Emojiuri Jumbo", + "adjust_the_size_of_emojis_sent_without_text": "SchimbaΜ† maΜ†rimea emojiurilor trimise faΜ‚raΜ† text.", + "privacy_and_security": "ConfidenΘ›ialitate Θ™i Securitate", + "blur_media": "EstompazaΜ† media.", + "blurs_images_and_videos_in_the_timeline": "EstompeazaΜ† imaginile si videourile din mesaje.", + "blur_avatars": "EstompeazaΜ† avatar-uri", + "blurs_user_profile_pictures_and_room_icons": "EstompeazaΜ† imaginile de profil si iconitΜ¦ele camerelor.", + "blur_emotes": "EstompeazaΜ† emoticoane", + "blurs_emoticons_within_messages": "EstompeazaΜ† emoticoanele din mesaje.", + "identity": "Identitate", + "colorful_names": "Nume Colorate Aleatoriu", + "assign_unique_colors_to_users_based_on_their_id_does_not_override_room_spac": "Atribuie culori unici utilizatorilor pe baza ID-ului lor. AceastaΜ† setare nu va inlocuii culorile atribuite de cameraΜ†/spatΜ¦iu, dar va inlocuii culoarea rolului de bazaΜ†", + "show_pronoun_pills": "ArataΜ† pilule de pronume", + "display_user_pronouns_in_the_message_timeline": "Arata pronumele utilizatorilor in mesaje", + "max_pronoun_pills": "NumaΜ†r maxim de pilule de pronume", + "maximum_number_of_pronoun_pills": "NumaΜ†rul maxim de pilule de pronume prezente de persoanaΜ† in conversatie. Alte pronume apar dupaΜ† pilula intitulataΜ† '...'", + "max_pronoun_pill_length": "Lungimea maximaΜ† a pronumelor de profil", + "maximum_pronoun_pill_length": "NumaΜ†rul maxim de litere in fiecare pronume iΜ‚nainte de a fi trunchiataΜ†.", + "pronoun_pills_for_all": "Pilule de pronume pentru toataΜ† lumea", + "attempts_to_convert_pronouns_in_names_into_pills_e_g_they_them_or_it_its_tu": "IΜ‚ncearcaΜ† sa transformi pronumele din nume in pilule (ex. [el/lui] sau [ea/iei] este transformat in pilulaΜ†).", + "render_custom_profile_cards": "RedaΜ† carduri de profil personalizate.", + "choose_whose_profile_card_colors_to_show_everyone_with_a_scheme_only_light": "Alege caΜ‚nd sa se redea cardul de profil: pentru toataΜ† lumea, doar teme iΜ‚ntunecate, doar teme luminoase, sau nu le reda deloc.", + "render_global_username_colors": "RedaΜ† culorile generale numelor persoanelor", + "display_the_username_colors_anyone_can_set_in_their_account_settings": "RedaΜ† culorile numelor tuturor persoanelor care au fost setate de acestea.", + "render_space_room_username_colors": "RedaΜ† culorile numelor setate de CameraΜ†/SpatΜ¦iu.", + "display_the_username_colors_that_can_be_set_with_color": "RedaΜ† culorile numelor care pot fi setate cu /color.", + "render_space_room_fonts": "RedaΜ† fonturile Camerei/SpatΜ¦iului.", + "display_the_username_fonts_that_can_be_set_with_font": "RedaΜ† fontul numelor care pot fi setate cu /font.", + "consistent_icon_style": "Stil al icoanelor consistent.", + "harmonize_icon_appearance_with_background_fill": "ArmonizeazaΜ† forma icoanelor cu o culoare de fundal.", + "extra_small": "Foarte mic", + "none_same_size_as_text": "BazaΜ† (aceasΜ¦i maΜ†rime ca textul)", + "small": "Mic", + "normal": "Normal", + "large": "Mari", + "extra_large": "Foarte mari", + "language_specific_pronouns": "Pronume specifice limbii", + "show_pronouns_only_in_selected_language": "RedaΜ† doar pronumele in limba selectataΜ†", + "if_enabled_pronouns_are_only_shown_when_they_match_your_selected_language_t": "DacaΜ† activat, doar pronumele in limba selectataΜ† de tine vor apaΜ†rea. Asta ajuta dacaΜ† contactele tale au setata pronume in limbi diferite. Nu afecteaza cum pronumele setate de tine sunt iΜ‚mpaΜ†rtaΜ†sΜ¦ite.", + "selected_language_for_pronouns": "Limbi selectate pentru pronume.", + "the_language_to_show_pronouns_for_when_the_above_setting_is_enabled": "Limbile in care pronumele vor fi redate pentru setarea de asupra.", + "language_code_e_g_en_de_en_de": "Codul limbii (ex. 'ro', 'de', 'ro,de')" + }, + "General": { + "formatting": "Formatare", + "month": "Luna", + "day_of_the_week": "Ziua saΜ†ptaΜ†maΜ†nii", + "day_of_the_week_sunday_0": "Ziua saΜ†ptaΜ†maΜ†nii (DuminicaΜ† = 0)", + "two_letter_day_name": "Numele zilei cu 2 litere", + "short_day_name": "Numele zilei scurt", + "full_day_name": "Numele zilei iΜ‚ntreg", + "day_of_the_month": "Ziua din lunaΜ†", + "full_month_name": "Numele iΜ‚ntreg al lunii", + "short_month_name": "Numele scurt al lunii", + "the_month": "Luna", + "two_digit_month": "Luna cu 2 cifre", + "four_digit_year": "Anul cu 4 cifre", + "two_digit_year": "Anul cu 2 cifre" + } + }, + "RoomView": { + "failed_to_load_history": "IΜ‚ncaΜ†rcarea istoriei a esΜ¦uat.", + "failed_to_load_messages": "IΜ‚ncaΜ†rcarea mesajelor a esΜ¦uat.", + "jump_to_unread": "Sari la necitit", + "mark_as_read": "NoteazaΜ† ca Citit", + "new_messages": "Mesaje Noi", + "jump_to_latest": "Sari la ultimele mesaje", + "call_started_by": "Apel iΜ‚nceput de", + "start_voice_call": "IΜ‚ncepe apel audio", + "typing": { + "are_typing": " scriu...", + "typing_sep_word": " sΜ¦i " + }, + "is_typing": "scrie...", + "close_threads": "IΜ‚nchide fire", + "search_threads": "CautaΜ† fire...", + "clear_search": "CuraΜ†tΜ¦aΜ† caΜ†utarea", + "Threads": { + "no_threads_match_your_search": "Nici un fir nu se potrivesΜ¦te cautaΜ†rii.", + "no_threads_yet": "IΜ‚nca nu sunt fire.", + "jump": "Sari", + "threads": "Fire", + "thread": "Fir", + "no_replies_yet_start_the_thread_below": "IΜ‚ncaΜ† nu are raΜ†spunsuri. IΜ‚ncepe un fir de mesaje!" + }, + "join_new_room": "AlaΜ†turaΜ†te unei camere noi", + "this_room_has_been_replaced_and_is_no_longer_active": "AceasaΜ† camera a fost schimbataΜ† sΜ¦i nu mai este activaΜ†.", + "failed_to_join_replacement_room": "IΜ‚ncercarea de a te alaΜ†tura camerei noi a esΜ¦uat!", + "open_new_room": "Deschide CameraΜ† nouaΜ†", + "scroll_to_top": "DeruleazaΜ† iΜ‚n Sus", + "Message": { + "pin_message": "FixeazaΜ† Mesajul", + "unpin_message": "Scoata fixarea Mesajului", + "forwarded_private_message": "Mesaj privat redirectΜ¦ionat", + "forwarded_from_earlier_in_this_room": "RedirectΜ¦ionat dintr-un mesaj mai vechi din aceastaΜ† cameraΜ†", + "forwarded_from_another_room": "RedirectΜ¦ionat din altaΜ† cameraΜ†", + "jump_to_original": "du-te la original", + "failed_to_send": "Trimitere esΜ¦uataΜ†.", + "only_you_can_see_this": "Doar tu potΜ¦i vedea asta.", + "edit_message": "EditeazaΜ† mesajul", + "Editor": { + "edit_message": "EditeazaΜ† mesajul..." + } + }, + "Reactions": { + "reacted_with": "ReactΜ¦ionat cu" + } + }, + "RoomInput": { + "recording_duration": "Lungime iΜ‚nregistrare:", + "EmojiBoard": { + "no_results_found": "Nu un rezultat", + "search_results": "Rezultate caΜ†utare", + "personal_pack": "Pachet Personal", + "unknown_pack": "Pachet Necunoscut" + } + }, + "DevTools": { + "json_content": "ContΜ¦inut JSON", + "account_data": "Datele contului", + "developer_tools": "Unelete Dezvoltatori" + } +} diff --git a/public/locales/ro/room/create.json b/public/locales/ro/room/create.json new file mode 100644 index 0000000000..5f0c32455d --- /dev/null +++ b/public/locales/ro/room/create.json @@ -0,0 +1,19 @@ +{ + "versions": "Versiuni", + "founders": "Fondatori", + "special_privileged_users_can_be_assigned_during_creation_these_users_have_e": "In durata creaΜ†ri, anumite persoane pot fi privilegiate. Aceste persoana au un control ridicat si pot fi modificate doar cu o modernizare a camerei.", + "no_suggestions": "Nici o sugestie", + "please_provide_the_user_id_and_hit_enter": "Oferiti ID-ul de utilizator si apaΜ†satΜ¦i Enter.", + "only_member_of_parent_space_can_join": "Doar membrii a spatΜ¦iului paΜ†rinte se pot alaΜ†tura.", + "private": "PrivataΜ†", + "only_people_with_invite_can_join": "Doar persoanele cu o invitatΜ¦ie se pot alaΜ†tura.", + "anyone_with_the_address_can_join": "Oricine are adresa se poate alaΜ†tura.", + "public": "PublicaΜ†", + "address_optional": "AdresaΜ† (OptΜ¦ionalaΜ†)", + "pick_an_unique_address_to_make_it_discoverable": "Alege o adresaΜ† unicaΜ† saΜ† poataΜ† fi comunitatea descoperitaΜ†.", + "this_address_is_already_taken_please_select_a_different_one": "AceastaΜ† adresaΜ† este deja luataΜ†. SelectatΜ¦i una diferitaΜ†.", + "voice_room": "CameraΜ† vocalaΜ†", + "live_audio_and_video_conversations": "; conversatΜ¦ii audio-video live.", + "chat_room": "CameraΜ† discutΜ¦ii", + "messages_photos_and_videos": "; Mesaje, fotografii, and videoclipuri." +} diff --git a/public/locales/ro/room/direct/invite.json b/public/locales/ro/room/direct/invite.json new file mode 100644 index 0000000000..cf7ade5019 --- /dev/null +++ b/public/locales/ro/room/direct/invite.json @@ -0,0 +1,7 @@ +{ + "direct_message_invite_prompt": "Aceasa este o conversatΜ¦ie directaΜ†, menitaΜ† pentru discutΜ¦ii iΜ‚ntre douaΜ† persoane, DoritΜ¦i sa o tranformatΜ¦i intr-un group de conversatΜ¦ie iΜ‚nainte de a continua?", + "failed_to_convert_direct_message_to_room": "Transformarea convesatΜ¦iei directe in conversatΜ¦ie de grup a esΜ¦uat!", + "convert_to_group_chat_and_invite": "TransformaΜ† in grup sΜ¦i invitaΜ†", + "invite_to_direct_message_anyway": "InvitaΜ† la mesaje directe oricum", + "invite_another_member": "InvitaΜ† altaΜ† persoana" +} diff --git a/public/locales/ro/settings/device_verification.json b/public/locales/ro/settings/device_verification.json new file mode 100644 index 0000000000..1d377212cc --- /dev/null +++ b/public/locales/ro/settings/device_verification.json @@ -0,0 +1,28 @@ +{ + "error_uia_action_without_data": "Eroare neasΜ¦teptataΜ†! Actiunea UIA este facutaΜ† faraΜ† date.", + "authentication_failed_failed_to_setup_device_verification": "Autentificare esΜ¦uataΜ†! Verificarea dispozitivului a esΜ¦uat.", + "unexpected_error_crypto_module_not_found": "Eroare neasΜ¦teptataΜ†! Modulul Crypto nu poate fi accessat!", + "unexpected_error_failed_to_create_recovery_key": "Eroare neasΜ¦teptataΜ†! Incercarea de a crea o cheie de rezervaΜ† a esΜ¦uat.", + "generate_a_recovery_key": "GeneratΜ¦i o cheie de recuperare pentru a iΜ‚tΜ¦i verifica identitatea in cazul in care nu ai access la alte dispozitive. AditΜ¦ional, seteazaΜ† o parolaΜ† ca o alternativaΜ† mai usΜ¦oaraΜ† de amintit.", + "optional_passphrase": "ParolaΜ† (optionalaΜ†)", + "authentication_steps_to_perform_this_action_are_not_supported_by_client": "PasΜ¦i pentru autentificare nu sunt acceptatΜ¦i de aceastaΜ† aplicatΜ¦ie.", + "store_the_recovery_key_in_a_safe_place_for_future_use_as_you_will_need_it_t": "SalveazaΜ† cheia de recuperare intr-un loc sigur pentru a fi accesataΜ† in viitor. Vei avea nevoie de ea sa iΜ‚tΜ¦i verifici identitatea in cazul in care nu ai acces la alte dispozitive.", + "setup_device_verification": "ConfigureazaΜ† verificare dispozitivelor.", + "reset_device_verification": "Reseteaza Identitea de verificare a dispozitivelor", + "resetting_device_verification_is_permanent": "Resetarea verificaΜ†rii dispozitivelor este permanentaΜ†!", + "anyone_you_have_verified_with_will_see_security_alerts_and_your_encryption": "ToataΜ† lumea cu care ai vorbit va primii o alertaΜ† de securitate si encriptia de rezervaΜ† va fi pierdutaΜ†. Aproape sigur nu vrei saΜ† faci asta, singura situatΜ¦ie iΜ‚n care ai vrea saΜ† faci asta este daca ai pierdut", + "and_every_device_you_can_verify_from": "sΜ¦i orice alt dispozitiv cu care ai putea verifica.", + "please_accept_the_request_from_other_device": "AcceptatΜ¦i cererea dintr-un alt dispozitiv.", + "click_accept_to_start_the_verification_process": "Apasa pe 'AcceptaΜ†' pentru a iΜ‚ncepe procesul de verificare.", + "waiting_for_request_to_be_accepted": "AsteptaΜ†m ca cererea saΜ† fie acceptataΜ†...", + "confirm_the_emoji_below_are_displayed_on_both_devices_in_the_same_order": "ConfirmaΜ† ca emoticoanele prezentate ulterior sunt aceleasΜ¦i pe ambele dispozitive, sΜ¦i iΜ‚n aceasΜ¦i ordine:", + "they_match": "Se potrivesc", + "do_not_match": "Sunt diferite", + "starting_verification_using_emoji_comparison": "IΜ‚ncepe verificare prin comparare de emoticoane...", + "your_device_is_verified": "Dispozitivul acesta e verificat.", + "verification_has_been_canceled": "Verificarea a forst opritaΜ†.", + "device_verification": "Verificare dispozitiv", + "unexpected_error_verification_is_started_but_verifier_is_missing": "Eroare neasΜ¦teptataΜ†! Verificarea a iΜ‚nceput dar verificatorul a dispaΜ†rut.", + "verification_request_has_been_accepted": "Cererea de verificare a fost acceptataΜ†.", + "waiting_for_the_response_from_other_device": "AsΜ¦teptaΜ†m pentru un raΜ†spuns de la celaΜ†lalt dispozitiv..." +} diff --git a/public/locales/ro/settings/experimental.json b/public/locales/ro/settings/experimental.json new file mode 100644 index 0000000000..4b2a8c2df5 --- /dev/null +++ b/public/locales/ro/settings/experimental.json @@ -0,0 +1,22 @@ +{ + "save_bandwidth_for_sticker_and_emoji_images": "ReducetΜ¦i Consumul retΜ¦elei pentru stickere si emoticoane", + "enable_bandwidth_saving_for_stickers_and_emojis": "Porneste salvare de 'bandwidth' pentru stickere si emoticoane", + "if_enabled_sticker_and_emoji_images_will_be_optimized_to_save_bandwidth_thi": "DacaΜ† pornit, stickerele si emoticoanele vor fi optimizate pentru a consuma mai putΜ¦in internet. Asta ajutaΜ† consumul de date caΜ‚nd vaΜ†zaΜ†nd aceste imagini, dar cresΜ¦te costul de computare a server-ului.", + "personas_per_message_profiles": "PersonalitaΜ†tΜ¦i. (Profiluri per-mesaj)", + "show_personas_tab": "PrezintaΜ† meniul de PersonalitaΜ†tΜ¦i", + "enables_the_personas_tab_in_the_settings_menu_for_per_message_profiles": "Adauga meniul de personalitaΜ†tΜ¦i in bara de setaΜ†ri pentru profile specifice fiecaΜ†rui mesaj.", + "experimental": "Experimental", + "the_features_listed_below_may_be_unstable_or_incomplete": "OptΜ¦iunile listate ulterior pot fi instabile sau incomplete,", + "use_at_your_own_risk": "folosesΜ¦te la riscul vostru", + "please_report_any_new_issues_potentially_caused_by_these_features": "VaΜ† rugaΜ†m sa raportati orice probleme noi cauzate potentΜ¦ial de aceste optΜ¦iuni! Mersi!", + "enable_sharing_of_encrypted_history": "ActiveazaΜ† iΜ‚mpaΜ†rtaΜ†sΜ¦irea de Istorie CriptataΜ†.", + "enable_the_sharehistory_command": "ActiveazaΜ† comanda de /sharehistory", + "if_enabled_this_command_will_allow_users_to_share_encrypted_history_with_ot": "DacaΜ† activataΜ†, aceastaΜ† optiune va permite utilizatorilor saΜ† impaΜ†rtasΜ¦eascaΜ† istoria criptataΜ† cu altΜ¦i utilizatori noi, per MSC4268.", + "disable_sharehistory_command": "DezactiveazaΜ† comanda /sharehistory", + "enable_sharehistory_command": "ActiveazaΜ† comanda /sharehistory", + "enable_media_galleries_support": "ActiveazaΜ† suportul pentru galerii de media", + "enable_media_galleries_title": "ActiveazaΜ† galeriile media", + "enable_media_galleries_description": "DacaΜ† activat, mai multe atasΜ¦amente vor fi trimise intr-un singur mesaj, pe baza MSC4272. Incompatibil cu alte aplicatii care nu mermit galerii", + "disable_media_galleries": "DezactiveazaΜ† galeriile media", + "enable_media_galleries": "ActiveazaΜ† galeriile media" +} diff --git a/public/locales/ro/settings/keyboard_shortcuts.json b/public/locales/ro/settings/keyboard_shortcuts.json new file mode 100644 index 0000000000..1ee207d241 --- /dev/null +++ b/public/locales/ro/settings/keyboard_shortcuts.json @@ -0,0 +1,16 @@ +{ + "search_for_messages": "CautaΜ† mesaje", + "jump_to_the_highest_priority_unread_room": "Du-te la camera cu cea mai ridicata prioritate de necitire", + "go_to_next_unread_room_cycle": "Du-te la urmaΜ†toarea camera necititaΜ† (urmeazaΜ† un ciclu)", + "go_to_previous_unread_room_cycle": "Go to previous unread room (urmeazaΜ† un ciclu)", + "undo_in_message_editor": "SΜ¦terge iΜ‚n editorul de mesaje", + "redo_in_message_editor": "Reface iΜ‚n editorul de mesaje", + "seach_and_go_to_room": "CautaΜ† sΜ¦i du-te la cameraΜ†", + "open_sticker_picker": "Deschide pachet de stickere", + "bold": "IΜ‚ngrosΜ¦at", + "italic": "Italic", + "underline": "Subliniat", + "general": "General", + "navigation": "NavigatΜ¦ie", + "messages": "Mesaje" +} diff --git a/public/locales/ro/settings/persona.json b/public/locales/ro/settings/persona.json new file mode 100644 index 0000000000..93dd8e48c7 --- /dev/null +++ b/public/locales/ro/settings/persona.json @@ -0,0 +1,21 @@ +{ + "limited_compatibility_with_pluralkit_like_functions": "Compatibilitate limitataΜ† cu functii similare cu PluralKit", + "enable_pk_commands": "ActiveazaΜ† comenzi PK", + "if_enabled_it_will_enable_a_few_pk_style_commands_currently_verry_limited": "DacaΜ† activat, va permite folosirea de comenzi in stil PK, deocamdataΜ† foarte limitate", + "disable_pk_commands": "dezactiveazaΜ† comenzile pk;", + "enable_pks_commands": "activeazaΜ† comenzile pk;", + "enable_shorthands": "ActiveazaΜ† scurtaΜ†turi", + "if_enabled_you_can_use_shorthands_to_use_a_persona_for_one_message_only_eg": "DacaΜ† activat, vei putea folosii scuraturi pentru a folosi personalitaΜ†tΜ¦i pentru un mesaj (ex. '✨:test')", + "disable_checking_typed_messages_for_shorthands": "dezactiveazaΜ† verificarea mesajelor scrise pentru scurataturi", + "enable_checking_typed_messages_for_shorthands": "activeaza verificarea mesajelor scrise pentru scurataturi", + "profile_id": "ID profil:", + "pronouns": "Pronume:", + "reset_pronouns": "ReseteazaΜ† Pronumele", + "display_name": "Nume AfisΜ¦at:", + "delete_profile": "SΜ¦terge profilul {{profileId}}", + "save_profile": "SalveazaΜ† schimbaΜ†rile pentru {{profileId}}", + "save_profile_button_area": "Zona butonului de salvare a profilului {{profileId}}", + "pronouns_for": "Pronumele profilului {{profileId}}", + "avatar_for": "Avatarul profilului {{profileId}}", + "display_name_for": "Numele afisΜ¦at pentru {{profileId}}" +} diff --git a/public/locales/ro/settings/profile.json b/public/locales/ro/settings/profile.json new file mode 100644 index 0000000000..d85dda76aa --- /dev/null +++ b/public/locales/ro/settings/profile.json @@ -0,0 +1,8 @@ +{ + "avatar_and_upload": "Imagine de profil si iΜ‚ncaΜ†rcare", + "profile_avatar": "Imagine de profil", + "avatar_fallback": "Imagine de rezervaΜ†", + "upload_avatar_image": "IΜ‚ncarcaΜ† imagine de profil", + "display_name_input": "AfisΜ¦ introducere nume de profil", + "reset_display_name": "ReseteazaΜ† numele afisΜ¦at" +} diff --git a/src/app/components/DeviceVerificationSetup.tsx b/src/app/components/DeviceVerificationSetup.tsx index 3ce6be1463..2320dbb357 100644 --- a/src/app/components/DeviceVerificationSetup.tsx +++ b/src/app/components/DeviceVerificationSetup.tsx @@ -15,7 +15,7 @@ import { useAlive } from '$hooks/useAlive'; import { PasswordInput } from './password-input'; import { ActionUIA, ActionUIAFlowsLoader } from './ActionUIA'; import { UseStateProvider } from './UseStateProvider'; -import { t } from 'i18next'; +import { useTranslation } from 'react-i18next'; type UIACallback = ( authDict: AuthDict | null @@ -58,14 +58,8 @@ function makeUIAAction( return action; } -function renderUnsupportedUIAFlow() { - return ( - - {t( - 'Settings.device_verification.authentication_steps_to_perform_this_action_are_not_supported_by_client' - )} - - ); +function renderUnsupportedUIAFlow(unsupportedFlow: string) { + return {unsupportedFlow}; } type SetupVerificationUIAProps = { @@ -92,6 +86,7 @@ type SetupVerificationProps = { function SetupVerification({ onComplete }: Readonly) { const mx = useMatrixClient(); const alive = useAlive(); + const { t } = useTranslation(['settings/device_verification', 'general']); const [uiaAction, setUIAAction] = useState>(); const [nextAuthData, setNextAuthData] = useState(); // null means no next action. @@ -99,7 +94,7 @@ function SetupVerification({ onComplete }: Readonly) { const handleAction = useCallback( async (authDict: AuthDict) => { if (!uiaAction) { - throw new Error(t('Settings.device_verification.error_uia_action_without_data')); + throw new Error(t('error_uia_action_without_data')); } if (alive()) { setNextAuthData(null); @@ -110,7 +105,7 @@ function SetupVerification({ onComplete }: Readonly) { setNextAuthData(authData); } }, - [uiaAction, alive] + [uiaAction, alive, t] ); const resetUIA = useCallback(() => { @@ -142,36 +137,25 @@ function SetupVerification({ onComplete }: Readonly) { if (alive()) { setUIAAction(action); } else { - reject( - new Error( - t( - 'Settings.device_verification.authentication_failed_failed_to_setup_device_verification' - ) - ) - ); + reject(new Error(t('authentication_failed_failed_to_setup_device_verification'))); } return; } reject(error); }); }), - [alive, resetUIA] + [alive, resetUIA, t] ); const [setupState, setup] = useAsyncCallback( useCallback( async (passphrase) => { const crypto = mx.getCrypto(); - if (!crypto) - throw new Error( - t('Settings.device_verification.unexpected_error_crypto_module_not_found') - ); + if (!crypto) throw new Error(t('unexpected_error_crypto_module_not_found')); const recoveryKeyData = await crypto.createRecoveryKeyFromPassphrase(passphrase); if (!recoveryKeyData.encodedPrivateKey) { - throw new Error( - t('Settings.device_verification.unexpected_error_failed_to_create_recovery_key') - ); + throw new Error(t('unexpected_error_failed_to_create_recovery_key')); } clearSecretStorageKeys(); @@ -189,7 +173,7 @@ function SetupVerification({ onComplete }: Readonly) { onComplete(recoveryKeyData.encodedPrivateKey); }, - [mx, onComplete, authUploadDeviceSigningKeys] + [mx, onComplete, authUploadDeviceSigningKeys, t] ) ); @@ -226,13 +210,9 @@ function SetupVerification({ onComplete }: Readonly) { return ( - - {t('Settings.device_verification.generate_a_recovery_key')} - + {t('generate_a_recovery_key')} - - {t('Settings.device_verification.optional_passphrase')} - + {t('optional_passphrase')} {setupState.status === AsyncStatus.Error && ( - {setupState.error ? setupState.error.message : t('General.unexpected_error')} + + {setupState.error ? setupState.error.message : t('unexpected_error', { ns: 'general' })} + )} {nextAuthData !== null && uiaAction && ( + renderUnsupportedUIAFlow( + t('authentication_steps_to_perform_this_action_are_not_supported_by_client') + ) + } > {renderSetupVerificationUIA} @@ -264,6 +250,7 @@ type RecoveryKeyDisplayProps = { }; function RecoveryKeyDisplay({ recoveryKey }: Readonly) { const [show, setShow] = useState(false); + const { t } = useTranslation(['settings/device_verification', 'general']); const handleCopy = () => { copyToClipboard(recoveryKey); @@ -281,12 +268,10 @@ function RecoveryKeyDisplay({ recoveryKey }: Readonly) return ( - {t( - 'Settings.device_verification.store_the_recovery_key_in_a_safe_place_for_future_use_as_you_will_need_it_t' - )} + {t('store_the_recovery_key_in_a_safe_place_for_future_use_as_you_will_need_it_t')} - {t('General.recovery_key')} + {t('recovery_key', { ns: 'general' })} ) {safeToDisplayKey} setShow(!show)} variant="Secondary" radii="Pill"> - {show ? t('General.hide') : t('General.show')} + + {show ? t('hide', { ns: 'general' }) : t('show', { ns: 'general' })} +
@@ -323,6 +310,7 @@ type DeviceVerificationSetupProps = { export const DeviceVerificationSetup = forwardRef( ({ onCancel }, ref) => { const [recoveryKey, setRecoveryKey] = useState(); + const { t } = useTranslation(['settings/device_verification']); return ( @@ -335,7 +323,7 @@ export const DeviceVerificationSetup = forwardRef - {t('Settings.device_verification.setup_device_verification')} + {t('setup_device_verification')} {composerIcon(X)} @@ -358,6 +346,7 @@ type DeviceVerificationResetProps = { export const DeviceVerificationReset = forwardRef( ({ onCancel }, ref) => { const [reset, setReset] = useState(false); + const { t } = useTranslation(['settings/device_verification', 'general']); return ( @@ -370,7 +359,7 @@ export const DeviceVerificationReset = forwardRef - {t('Settings.device_verification.reset_device_verification')} + {t('reset_device_verification')} {composerIcon(X)} @@ -392,19 +381,16 @@ export const DeviceVerificationReset = forwardRef βœ‹πŸ§‘β€πŸš’πŸ€š + {t('resetting_device_verification_is_permanent')} - {t('Settings.device_verification.resetting_device_verification_is_permanent')} - - - {t( - 'Settings.device_verification.anyone_you_have_verified_with_will_see_security_alerts_and_your_encryption' - )}{' '} - {t('General.recovery_key')} / {t('General.recovery_passphrase')}{' '} - {t('Settings.device_verification.and_every_device_you_can_verify_from')} + {t('anyone_you_have_verified_with_will_see_security_alerts_and_your_encryption')}{' '} + {t('recovery_key', { ns: 'general' })} /{' '} + {t('recovery_passphrase', { ns: 'general' })}{' '} + {t('and_every_device_you_can_verify_from')}
)} diff --git a/src/app/components/create-room/AdditionalCreatorInput.tsx b/src/app/components/create-room/AdditionalCreatorInput.tsx index 5b651290a1..4f82f26d07 100644 --- a/src/app/components/create-room/AdditionalCreatorInput.tsx +++ b/src/app/components/create-room/AdditionalCreatorInput.tsx @@ -27,7 +27,7 @@ import type { UseAsyncSearchOptions } from '$hooks/useAsyncSearch'; import { useAsyncSearch } from '$hooks/useAsyncSearch'; import { highlightText, makeHighlightRegex } from '$plugins/react-custom-html-parser'; import { SettingTile } from '$components/setting-tile'; -import { t } from 'i18next'; +import { useTranslation } from 'react-i18next'; export const useAdditionalCreators = (defaultCreators?: string[]) => { const mx = useMatrixClient(); @@ -83,6 +83,7 @@ export function AdditionalCreatorInput({ const mx = useMatrixClient(); const [menuCords, setMenuCords] = useState(); const directUsers = useDirectUsers(); + const { t } = useTranslation('room/create'); const [validUserId, setValidUserId] = useState(); const filteredUsers = useMemo( @@ -147,10 +148,8 @@ export function AdditionalCreatorInput({ return ( @@ -265,10 +264,10 @@ export function AdditionalCreatorInput({ gap="100" > - {t('RoomCreate.no_suggestions')} + {t('no_suggestions')} - {t('RoomCreate.please_provide_the_user_id_and_hit_enter')} + {t('please_provide_the_user_id_and_hit_enter')} )} diff --git a/src/app/components/create-room/CreateRoomAccessSelector.tsx b/src/app/components/create-room/CreateRoomAccessSelector.tsx index 2a2c44fc51..61faa66e2c 100644 --- a/src/app/components/create-room/CreateRoomAccessSelector.tsx +++ b/src/app/components/create-room/CreateRoomAccessSelector.tsx @@ -4,7 +4,7 @@ import { Check, sizedIcon } from '$components/icons/phosphor'; import { SequenceCard } from '$components/sequence-card'; import { SettingTile } from '$components/setting-tile'; import { CreateRoomAccess } from './types'; -import { t } from 'i18next'; +import { useTranslation } from 'react-i18next'; type CreateRoomAccessSelectorProps = { value?: CreateRoomAccess; @@ -20,6 +20,7 @@ export function CreateRoomAccessSelector({ disabled, getIcon, }: CreateRoomAccessSelectorProps) { + const { t } = useTranslation('room/create'); return ( {canRestrict && ( @@ -40,7 +41,7 @@ export function CreateRoomAccessSelector({ > Restricted - {t('RoomCreate.only_member_of_parent_space_can_join')} + {t('only_member_of_parent_space_can_join')} @@ -60,9 +61,9 @@ export function CreateRoomAccessSelector({ before={getIcon(CreateRoomAccess.Private)} after={value === CreateRoomAccess.Private && sizedIcon(Check)} > - {t('RoomCreate.private')} + {t('private')} - {t('RoomCreate.only_people_with_invite_can_join')} + {t('only_people_with_invite_can_join')} @@ -81,9 +82,9 @@ export function CreateRoomAccessSelector({ before={getIcon(CreateRoomAccess.Public)} after={value === CreateRoomAccess.Public && sizedIcon(Check)} > - {t('RoomCreate.public')} + {t('public')} - {t('RoomCreate.anyone_with_the_address_can_join')} + {t('anyone_with_the_address_can_join')} diff --git a/src/app/components/direct-invite-prompt/DirectInvitePrompt.tsx b/src/app/components/direct-invite-prompt/DirectInvitePrompt.tsx index b5e91953d3..c706d569e5 100644 --- a/src/app/components/direct-invite-prompt/DirectInvitePrompt.tsx +++ b/src/app/components/direct-invite-prompt/DirectInvitePrompt.tsx @@ -15,7 +15,7 @@ import { } from 'folds'; import { composerIcon, X } from '$components/icons/phosphor'; import { stopPropagation } from '$utils/keyboard'; -import { t } from 'i18next'; +import { useTranslation } from 'react-i18next'; type DirectInvitePromptProps = { onCancel: () => void; @@ -32,6 +32,7 @@ export function DirectInvitePrompt({ converting, convertError, }: DirectInvitePromptProps) { + const { t } = useTranslation(['room/direct/invite', 'general']); return ( }> @@ -53,7 +54,7 @@ export function DirectInvitePrompt({ size="500" > - {t('Room.DirectInvite.invite_another_member')} + {t('invite_another_member')} {composerIcon(X)} @@ -61,10 +62,10 @@ export function DirectInvitePrompt({
- {t('Room.DirectInvite.direct_message_invite_prompt')} + {t('direct_message_invite_prompt')} {convertError && ( - {t('Room.DirectInvite.failed_to_convert_direct_message_to_room')} {convertError} + {t('failed_to_convert_direct_message_to_room')} {convertError} )} @@ -79,9 +80,7 @@ export function DirectInvitePrompt({ aria-disabled={converting} > - {converting - ? 'Converting...' - : t('Room.DirectInvite.convert_to_group_chat_and_invite')} + {converting ? 'Converting...' : t('convert_to_group_chat_and_invite')} diff --git a/src/app/components/message/Reply.tsx b/src/app/components/message/Reply.tsx index 1178b81b63..f19eff5243 100644 --- a/src/app/components/message/Reply.tsx +++ b/src/app/components/message/Reply.tsx @@ -277,7 +277,7 @@ export const Reply = as<'div', ReplyProps>( const ignoredUsers = useIgnoredUsers(); const isBlockedSender = !!sender && ignoredUsers.includes(sender); - const { t } = useTranslation(); + const { t } = useTranslation('general'); const parseMemberEvent = useMemberEventParser(); diff --git a/src/app/features/room/RoomViewTyping.tsx b/src/app/features/room/RoomViewTyping.tsx index 8d4560a522..7716325746 100644 --- a/src/app/features/room/RoomViewTyping.tsx +++ b/src/app/features/room/RoomViewTyping.tsx @@ -63,7 +63,7 @@ export const RoomViewTyping = as<'div', RoomViewTypingProps>( <> {typingNames[0]} - {t('RoomView.is_typing')} + {` ${t('RoomView.is_typing')}`} )} diff --git a/src/app/features/settings/Persona/PKCompat.tsx b/src/app/features/settings/Persona/PKCompat.tsx index de2ad52e0f..aee5d23abb 100644 --- a/src/app/features/settings/Persona/PKCompat.tsx +++ b/src/app/features/settings/Persona/PKCompat.tsx @@ -4,17 +4,16 @@ import { useSetting } from '$state/hooks/settings'; import { settingsAtom } from '$state/settings'; import { Box, Switch, Text } from 'folds'; import { SequenceCardStyle } from '../styles.css'; -import { t } from 'i18next'; +import { useTranslation } from 'react-i18next'; export function PKCompatSettings() { const [usePKCompat, setUsePKCompat] = useSetting(settingsAtom, 'pkCompat'); const [usePmpProxying, setUsePmpProxying] = useSetting(settingsAtom, 'pmpProxying'); + const { t } = useTranslation(['settings/persona']); return ( - - {t('Settings.Persona.limited_compatibility_with_pluralkit_like_functions')} - + {t('limited_compatibility_with_pluralkit_like_functions')} } /> } diff --git a/src/app/features/settings/Persona/PerMessageProfileEditor.tsx b/src/app/features/settings/Persona/PerMessageProfileEditor.tsx index b4de0e0a22..b5df38b394 100644 --- a/src/app/features/settings/Persona/PerMessageProfileEditor.tsx +++ b/src/app/features/settings/Persona/PerMessageProfileEditor.tsx @@ -18,7 +18,7 @@ import { import type { PronounSet } from '$utils/pronouns'; import { parsePronounsStringToPronounsSetArray } from '$utils/pronouns'; import { SequenceCardStyle } from '../styles.css'; -import { t } from 'i18next'; +import { useTranslation } from 'react-i18next'; /** * the props we use for the per-message profile editor, which is used to edit a per-message profile. This is used in the settings page when the user wants to edit a profile. @@ -46,6 +46,7 @@ export function PerMessageProfileEditor({ const [currentDisplayName, setCurrentDisplayName] = useState(displayName ?? ''); const [currentId, setCurrentId] = useState(profileId); const [newId, setNewId] = useState(profileId); + const { t } = useTranslation(['settings/persona', 'settings/profile', 'general']); console.warn(pronouns); @@ -228,7 +229,7 @@ export function PerMessageProfileEditor({ style={{ width: '100%', marginBottom: config.space.S200 }} > - {t('Settings.PerMessageProfiles.profile_id')} + {t('profile_id')} @@ -265,7 +266,7 @@ export function PerMessageProfileEditor({ overflow: 'visible', marginTop: 20, }} - aria-label={t('Settings.Profile.avatar_and_upload')} + aria-label={t('settings/profile:avatar_and_upload')} > ( - + p )} - alt={`Avatar for profile ${profileId}`} + alt={t('avatar_for', { profileId })} /> {uploadAtom && ( - {t('Settings.PerMessageProfiles.display_name')} + {t('display_name')} {menuIcon(X)} @@ -391,7 +392,7 @@ export function PerMessageProfileEditor({ alignSelf: 'flex-start', }} > - Pronouns: + {t('pronouns')} {menuIcon(X)} @@ -442,7 +443,7 @@ export function PerMessageProfileEditor({ flexShrink: 0, height: '100%', }} - aria-label={`Save button area for ${profileId}`} + aria-label={t('save_profile_button_area', { profileId })} > diff --git a/src/app/features/settings/cosmetics/Cosmetics.tsx b/src/app/features/settings/cosmetics/Cosmetics.tsx index 0cf62439b5..8dda0d7372 100644 --- a/src/app/features/settings/cosmetics/Cosmetics.tsx +++ b/src/app/features/settings/cosmetics/Cosmetics.tsx @@ -208,7 +208,7 @@ function IconSizeSettings() { } function SelectJumboEmojiSize() { - const { t } = useTranslation(); + const { t } = useTranslation('general'); const emojiSizeItems = [ { id: 'none', name: t('Settings.Cosmetics.none_same_size_as_text') }, @@ -287,7 +287,7 @@ function SelectJumboEmojiSize() { } function SelectRenderCustomProfileCards() { - const { t } = useTranslation(); + const { t } = useTranslation('general'); const profileCardRenderItems: { id: RenderUserCardsMode; name: string }[] = [ { id: 'both', name: t('Settings.Cosmetics.light_and_dark') }, { id: 'light', name: t('Settings.Cosmetics.light_only') }, @@ -365,7 +365,7 @@ function SelectRenderCustomProfileCards() { } function JumboEmoji() { - const { t } = useTranslation(); + const { t } = useTranslation('general'); return ( {t('Settings.Cosmetics.jumbo_emoji')} @@ -382,7 +382,7 @@ function JumboEmoji() { } function Privacy() { - const { t } = useTranslation(); + const { t } = useTranslation('general'); const [privacyBlur, setPrivacyBlur] = useSetting(settingsAtom, 'privacyBlur'); const [privacyBlurAvatars, setPrivacyBlurAvatars] = useSetting( settingsAtom, @@ -429,7 +429,7 @@ function Privacy() { } function IdentityCosmetics() { - const { t } = useTranslation(); + const { t } = useTranslation('general'); const [legacyUsernameColor, setLegacyUsernameColor] = useSetting( settingsAtom, 'legacyUsernameColor' diff --git a/src/app/features/settings/experimental/BandwithSavingEmojis.tsx b/src/app/features/settings/experimental/BandwithSavingEmojis.tsx index e815e881cf..9b0144084c 100644 --- a/src/app/features/settings/experimental/BandwithSavingEmojis.tsx +++ b/src/app/features/settings/experimental/BandwithSavingEmojis.tsx @@ -4,17 +4,18 @@ import { useSetting } from '$state/hooks/settings'; import { settingsAtom } from '$state/settings'; import { Box, Switch, Text } from 'folds'; import { SequenceCardStyle } from '../styles.css'; -import { t } from 'i18next'; +import { useTranslation } from 'react-i18next'; export function BandwidthSavingEmojis() { const [useBandwidthSaving, setUseBandwidthSaving] = useSetting( settingsAtom, 'saveStickerEmojiBandwidth' ); + const { t } = useTranslation(['settings/experimental']); return ( - {t('Settings.save_bandwidth_for_sticker_and_emoji_images')} + {t('save_bandwidth_for_sticker_and_emoji_images')} diff --git a/src/app/features/settings/experimental/Experimental.tsx b/src/app/features/settings/experimental/Experimental.tsx index a2f8d24c3c..2ea22c2a4c 100644 --- a/src/app/features/settings/experimental/Experimental.tsx +++ b/src/app/features/settings/experimental/Experimental.tsx @@ -11,25 +11,24 @@ import { Sync } from '../general'; import { SettingsSectionPage } from '../SettingsSectionPage'; import { BandwidthSavingEmojis } from './BandwithSavingEmojis'; import { MSC4268HistoryShare } from './MSC4268HistoryShare'; -import { t } from 'i18next'; import { MSC4274MediaGalleries } from './MSC4274MediaGalleries'; +import { useTranslation } from 'react-i18next'; function PersonaToggle() { const [showPersonaSetting, setShowPersonaSetting] = useSetting( settingsAtom, 'showPersonaSetting' ); + const { t } = useTranslation(['settings/experimental']); return ( - {t('Settings.personas_per_message_profiles')} + {t('personas_per_message_profiles')} } @@ -44,9 +43,10 @@ type ExperimentalProps = { requestClose: () => void; }; export function Experimental({ requestBack, requestClose }: Readonly) { + const { t } = useTranslation(['settings/experimental']); return ( @@ -58,10 +58,10 @@ export function Experimental({ requestBack, requestClose }: Readonly - {t('Settings.the_features_listed_below_may_be_unstable_or_incomplete')}{' '} - {t('Settings.use_at_your_own_risk')}. + {t('the_features_listed_below_may_be_unstable_or_incomplete')}{' '} + {t('use_at_your_own_risk')}.
- {t('Settings.please_report_any_new_issues_potentially_caused_by_these_features')} + {t('please_report_any_new_issues_potentially_caused_by_these_features')} } /> diff --git a/src/app/features/settings/experimental/MSC4268HistoryShare.tsx b/src/app/features/settings/experimental/MSC4268HistoryShare.tsx index 21f3b472b6..94e4df345b 100644 --- a/src/app/features/settings/experimental/MSC4268HistoryShare.tsx +++ b/src/app/features/settings/experimental/MSC4268HistoryShare.tsx @@ -4,17 +4,18 @@ import { useSetting } from '$state/hooks/settings'; import { settingsAtom } from '$state/settings'; import { Box, Switch, Text } from 'folds'; import { SequenceCardStyle } from '../styles.css'; -import { t } from 'i18next'; +import { useTranslation } from 'react-i18next'; export function MSC4268HistoryShare() { const [enabledMSC4268Command, setEnabledMSC4268Command] = useSetting( settingsAtom, 'enableMSC4268CMD' ); + const { t } = useTranslation(['settings/experimental']); return ( - {t('Settings.enable_sharing_of_encrypted_history')} + {t('enable_sharing_of_encrypted_history')} } diff --git a/src/app/features/settings/experimental/MSC4274MediaGalleries.tsx b/src/app/features/settings/experimental/MSC4274MediaGalleries.tsx index 397f9306c4..0128405b31 100644 --- a/src/app/features/settings/experimental/MSC4274MediaGalleries.tsx +++ b/src/app/features/settings/experimental/MSC4274MediaGalleries.tsx @@ -4,16 +4,18 @@ import { useSetting } from '$state/hooks/settings'; import { settingsAtom } from '$state/settings'; import { Box, Switch, Text } from 'folds'; import { SequenceCardStyle } from '../styles.css'; +import { useTranslation } from 'react-i18next'; export function MSC4274MediaGalleries() { const [enabledMediaGalleries, setEnabledMediaGalleries] = useSetting( settingsAtom, 'enableMediaGalleries' ); + const { t } = useTranslation(['settings/experimental']); return ( - Enable Media Galleries Support + {t('enable_media_galleries_support')} } /> diff --git a/src/app/features/settings/general/General.tsx b/src/app/features/settings/general/General.tsx index 259e281015..510a4bfcf8 100644 --- a/src/app/features/settings/general/General.tsx +++ b/src/app/features/settings/general/General.tsx @@ -435,7 +435,7 @@ function DateAndTime() { } function LanguageChange() { - const { i18n } = useTranslation(); + const { i18n } = useTranslation('general'); const languageOptions: SettingMenuOption[] = [ { value: '', label: 'System' }, diff --git a/src/app/features/settings/keyboard-shortcuts/KeyboardShortcuts.tsx b/src/app/features/settings/keyboard-shortcuts/KeyboardShortcuts.tsx index f1fd741dbf..ac0bba256a 100644 --- a/src/app/features/settings/keyboard-shortcuts/KeyboardShortcuts.tsx +++ b/src/app/features/settings/keyboard-shortcuts/KeyboardShortcuts.tsx @@ -7,7 +7,7 @@ import { Box, Scroll, Text, config } from 'folds'; import { PageContent } from '$components/page'; import { SettingsSectionPage } from '../SettingsSectionPage'; -import { t } from 'i18next'; +import { useTranslation } from 'react-i18next'; type ShortcutEntry = { keys: string; @@ -19,53 +19,53 @@ type ShortcutCategory = { shortcuts: ShortcutEntry[]; }; -function formatKey(key: string): string { - const isMac = - typeof navigator !== 'undefined' && navigator.platform.toUpperCase().indexOf('MAC') >= 0; - return key - .replace(/\bmod\b/g, isMac ? '⌘' : 'Ctrl') - .replace(/\balt\b/gi, isMac ? 'βŒ₯' : 'Alt') - .replace(/\bshift\b/gi, '⇧'); -} - +// uses translation keys to avoid initializing it too early const SHORTCUT_CATEGORIES: ShortcutCategory[] = [ { - name: 'General', - shortcuts: [{ keys: 'Ctrl+F / ⌘+F', description: 'Search for messages' }], + name: 'general', + shortcuts: [{ keys: 'Ctrl+F / ⌘+F', description: 'search_for_messages' }], }, { - name: 'Navigation', + name: 'navigation', shortcuts: [ { keys: 'Alt+N', - description: t('Settings.KeyboardShortcuts.jump_to_the_highest_priority_unread_room'), + description: 'jump_to_the_highest_priority_unread_room', }, { keys: 'Alt+Shift+Down', - description: t('Settings.KeyboardShortcuts.go_to_next_unread_room_cycle'), + description: 'go_to_next_unread_room_cycle', }, { keys: 'Alt+Shift+Up', - description: t('Settings.KeyboardShortcuts.go_to_previous_unread_room_cycle'), + description: 'go_to_previous_unread_room_cycle', }, - { keys: 'Ctrl+K / ⌘+K', description: 'Search and go to Room' }, + { keys: 'Ctrl+K / ⌘+K', description: 'seach_and_go_to_room' }, ], }, { - name: t('Settings.KeyboardShortcuts.messages'), + name: 'messages', shortcuts: [ - { keys: 'Ctrl+Z / ⌘+Z', description: t('Settings.KeyboardShortcuts.undo_in_message_editor') }, + { keys: 'Ctrl+Z / ⌘+Z', description: 'undo_in_message_editor' }, { keys: 'Ctrl+Shift+Z / ⌘+Shift+Z', - description: t('Settings.KeyboardShortcuts.redo_in_message_editor'), + description: 'redo_in_message_editor', }, - { keys: 'Ctrl+B / ⌘+B', description: t('Settings.KeyboardShortcuts.bold') }, - { keys: 'Ctrl+I / ⌘+I', description: t('Settings.KeyboardShortcuts.italic') }, - { keys: 'Ctrl+U / ⌘+U', description: t('Settings.KeyboardShortcuts.underline') }, - { keys: 'Ctrl+E / ⌘+E', description: 'Open Sticker Picker' }, + { keys: 'Ctrl+B / ⌘+B', description: 'bold' }, + { keys: 'Ctrl+I / ⌘+I', description: 'italic' }, + { keys: 'Ctrl+U / ⌘+U', description: 'underline' }, + { keys: 'Ctrl+E / ⌘+E', description: 'open_sticker_picker' }, ], }, ]; +function formatKey(key: string): string { + const isMac = + typeof navigator !== 'undefined' && navigator.platform.toUpperCase().indexOf('MAC') >= 0; + return key + .replace(/\bmod\b/g, isMac ? '⌘' : 'Ctrl') + .replace(/\balt\b/gi, isMac ? 'βŒ₯' : 'Alt') + .replace(/\bshift\b/gi, '⇧'); +} function ShortcutRow({ keys, description }: ShortcutEntry) { const parts = keys.split('/').map((k) => k.trim()); @@ -129,11 +129,13 @@ type KeyboardShortcutsProps = { requestClose: () => void; }; export function KeyboardShortcuts({ requestBack, requestClose }: KeyboardShortcutsProps) { + const { t } = useTranslation(['settings/keyboard_shortcuts']); + return ( @@ -144,14 +146,14 @@ export function KeyboardShortcuts({ requestBack, requestClose }: KeyboardShortcu {SHORTCUT_CATEGORIES.map((category) => ( - {category.name} + {t(category.name)}
{category.shortcuts.map((entry) => (
{entry.keys}
- +
))} diff --git a/src/app/hooks/timeline/useTimelineEventRenderer.tsx b/src/app/hooks/timeline/useTimelineEventRenderer.tsx index 3b8d639b66..2a0038d1fd 100644 --- a/src/app/hooks/timeline/useTimelineEventRenderer.tsx +++ b/src/app/hooks/timeline/useTimelineEventRenderer.tsx @@ -382,7 +382,7 @@ export function useTimelineEventRenderer({ }, utils: { htmlReactParserOptions, linkifyOpts, getMemberPowerTag, parseMemberEvent }, }: TimelineEventRendererOptions) { - const { t } = useTranslation(); + const { t } = useTranslation('general'); const { hiddenEventEdits, hiddenEventRedactionTimeline, diff --git a/src/app/i18n.ts b/src/app/i18n.ts index 977066f9f0..be7981354c 100644 --- a/src/app/i18n.ts +++ b/src/app/i18n.ts @@ -18,22 +18,22 @@ i18n // init i18next // for all options read: https://www.i18next.com/overview/configuration-options .init({ - // Prefer the browser / navigator language and avoid using cached localStorage value + defaultNS: 'general', + fallbackLng: 'en', + load: 'languageOnly', + interpolation: { + escapeValue: false, + }, detection: { - // prefer querystring first (e.g. ?lng=de), then storage,System then navigator, then html tag, path, subdomain order: ['querystring', 'localStorage', 'navigator', 'htmlTag', 'path', 'subdomain'], lookupQuerystring: 'lng', - // do not cache the detected language in localStorage to avoid stale overrides caches: ['localStorage'], }, - debug: false, - fallbackLng: 'en', - interpolation: { - escapeValue: false, // not needed for react as it escapes by default - }, - load: 'languageOnly', backend: { - loadPath: `${trimTrailingSlash(import.meta.env.BASE_URL)}/public/locales/{{lng}}.json`, + loadPath: `${trimTrailingSlash(import.meta.env.BASE_URL)}/public/locales/{{lng}}/{{ns}}.json`, + }, + react: { + useSuspense: false, }, }); From a1292dd24a29ee4bb9c5d929f5bf7a23abed95c6 Mon Sep 17 00:00:00 2001 From: Shea Date: Thu, 23 Jul 2026 03:21:15 +0300 Subject: [PATCH 23/23] add more settings localisations Signed-off-by: Shea --- public/locales/en/events.json | 12 + public/locales/en/general.json | 136 +------ public/locales/en/room/drawers/members.json | 5 + public/locales/en/room/drawers/reactions.json | 3 + public/locales/en/room/drawers/threads.json | 13 + public/locales/en/room/input.json | 9 + .../en/room/room-view/replaced-room.json | 6 + .../en/room/room-view/room-buttons.json | 3 + .../en/room/room-view/timeline-pills.json | 7 + public/locales/en/room/room-view/typing.json | 7 + public/locales/en/settings/appearance.json | 49 +++ public/locales/en/settings/dev_tools.json | 5 + public/locales/en/settings/general.json | 187 ++++++++++ public/locales/ro/events.json | 12 + public/locales/ro/general.json | 133 +------ public/locales/ro/room/create.json | 24 +- public/locales/ro/room/direct/invite.json | 10 +- public/locales/ro/room/drawers/members.json | 5 + public/locales/ro/room/drawers/reactions.json | 3 + public/locales/ro/room/drawers/threads.json | 14 + public/locales/ro/room/input.json | 9 + .../ro/room/room-view/replaced-room.json | 6 + .../ro/room/room-view/room-buttons.json | 3 + .../ro/room/room-view/timeline-pills.json | 7 + public/locales/ro/room/room-view/typing.json | 7 + public/locales/ro/settings/appearance.json | 49 +++ public/locales/ro/settings/dev_tools.json | 5 + public/locales/ro/settings/general.json | 186 ++++++++++ src/app/components/AccountDataEditor.tsx | 21 +- .../create-room/CreateRoomAliasInput.tsx | 9 +- .../create-room/CreateRoomTypeSelector.tsx | 11 +- src/app/components/emoji-board/EmojiBoard.tsx | 30 +- .../features/room/AudioMessageRecorder.tsx | 5 +- src/app/features/room/MembersDrawer.tsx | 22 +- src/app/features/room/RoomCallButton.test.tsx | 7 +- src/app/features/room/RoomCallButton.tsx | 5 +- src/app/features/room/RoomTimeline.tsx | 17 +- src/app/features/room/RoomTombstone.tsx | 13 +- src/app/features/room/RoomViewTyping.tsx | 28 +- src/app/features/room/ThreadBrowser.tsx | 18 +- src/app/features/room/ThreadDrawer.tsx | 12 +- src/app/features/room/message/Message.tsx | 27 +- .../room/reaction-viewer/ReactionViewer.tsx | 5 +- .../features/settings/cosmetics/Cosmetics.tsx | 99 +++--- .../cosmetics/LanguageSpecificPronouns.tsx | 17 +- .../settings/general/CallSoundSettings.tsx | 75 ++-- .../general/CallSoundSettingsCards.tsx | 29 +- src/app/features/settings/general/General.tsx | 336 ++++++++++-------- src/app/features/settings/settingsLink.ts | 1 + 49 files changed, 1059 insertions(+), 643 deletions(-) create mode 100644 public/locales/en/events.json create mode 100644 public/locales/en/room/drawers/members.json create mode 100644 public/locales/en/room/drawers/reactions.json create mode 100644 public/locales/en/room/drawers/threads.json create mode 100644 public/locales/en/room/input.json create mode 100644 public/locales/en/room/room-view/replaced-room.json create mode 100644 public/locales/en/room/room-view/room-buttons.json create mode 100644 public/locales/en/room/room-view/timeline-pills.json create mode 100644 public/locales/en/room/room-view/typing.json create mode 100644 public/locales/en/settings/appearance.json create mode 100644 public/locales/en/settings/dev_tools.json create mode 100644 public/locales/en/settings/general.json create mode 100644 public/locales/ro/events.json create mode 100644 public/locales/ro/room/drawers/members.json create mode 100644 public/locales/ro/room/drawers/reactions.json create mode 100644 public/locales/ro/room/drawers/threads.json create mode 100644 public/locales/ro/room/input.json create mode 100644 public/locales/ro/room/room-view/replaced-room.json create mode 100644 public/locales/ro/room/room-view/room-buttons.json create mode 100644 public/locales/ro/room/room-view/timeline-pills.json create mode 100644 public/locales/ro/room/room-view/typing.json create mode 100644 public/locales/ro/settings/appearance.json create mode 100644 public/locales/ro/settings/dev_tools.json create mode 100644 public/locales/ro/settings/general.json diff --git a/public/locales/en/events.json b/public/locales/en/events.json new file mode 100644 index 0000000000..9ca7331d97 --- /dev/null +++ b/public/locales/en/events.json @@ -0,0 +1,12 @@ +{ + "forwarded_private_message": "Forwarded private message", + "forwarded_from_earlier_in_this_room": "Forwarded from earlier in this room", + "forwarded_from_another_room": "Forwarded from another room", + "jump_to_original": "jump to original", + "failed_to_send": "Failed to send.", + "only_you_can_see_this": "Only you can see this.", + "edit_message": "Edit Message", + "Editor": { + "edit_message": "Edit message..." + } +} diff --git a/public/locales/en/general.json b/public/locales/en/general.json index 85d4cca31e..2936ad8602 100644 --- a/public/locales/en/general.json +++ b/public/locales/en/general.json @@ -29,141 +29,13 @@ "pronouns": "Pronouns", "unknown": "Unknown", "edit": "Edit", + "result_one": " result", + "result_other": " results", + "jump": "Jump", + "import": "Import", "Organisms": { "RoomCommon": { "changed_room_name": " changed room name" } - }, - "Settings": { - "Cosmetics": { - "light_and_dark": "Light & dark", - "light_only": "Light only", - "dark_only": "Dark only", - "off": "Off", - "jumbo_emoji": "Jumbo Emoji", - "jumbo_emoji_size": "Jumbo Emoji Size", - "adjust_the_size_of_emojis_sent_without_text": "Adjust the size of emojis sent without text.", - "privacy_and_security": "Privacy & Security", - "blur_media": "Blur Media", - "blurs_images_and_videos_in_the_timeline": "Blurs images and videos in the timeline.", - "blur_avatars": "Blur Avatars", - "blurs_user_profile_pictures_and_room_icons": "Blurs user profile pictures and room icons.", - "blur_emotes": "Blur Emotes", - "blurs_emoticons_within_messages": "Blurs emoticons within messages.", - "identity": "Identity", - "colorful_names": "Colorful Names", - "assign_unique_colors_to_users_based_on_their_id_does_not_override_room_spac": "Assign unique colors to users based on their ID. Does not override room/space custom colors. Will override default role colors.", - "show_pronoun_pills": "Show Pronoun Pills", - "display_user_pronouns_in_the_message_timeline": "Display user pronouns in the message timeline.", - "max_pronoun_pills": "Max Pronoun Pills", - "maximum_number_of_pronoun_pills": "Maximum number of pronoun pills shown per user in the timeline. Additional pronouns appear behind the ... pill.", - "max_pronoun_pill_length": "Max Pronoun Pill Length", - "maximum_pronoun_pill_length": "Maximum characters shown in each pronoun pill before truncation.", - "pronoun_pills_for_all": "Pronoun Pills for All", - "attempts_to_convert_pronouns_in_names_into_pills_e_g_they_them_or_it_its_tu": "Attempts to convert pronouns in names into pills (e.g. [they/them] or (it/its) turns into a pill).", - "render_custom_profile_cards": "Render Custom Profile Cards", - "choose_whose_profile_card_colors_to_show_everyone_with_a_scheme_only_light": "Choose whose profile card colors to show: everyone with a scheme, only light or dark schemes, or hide them.", - "render_global_username_colors": "Render Global Username Colors", - "display_the_username_colors_anyone_can_set_in_their_account_settings": "Display the username colors anyone can set in their account settings.", - "render_space_room_username_colors": "Render Space/Room Username Colors", - "display_the_username_colors_that_can_be_set_with_color": "Display the username colors that can be set with /color.", - "render_space_room_fonts": "Render Space/Room Fonts", - "display_the_username_fonts_that_can_be_set_with_font": "Display the username fonts that can be set with /font.", - "consistent_icon_style": "Consistent Icon Style", - "harmonize_icon_appearance_with_background_fill": "Harmonize icon appearance with background fill", - "extra_small": "Extra Small", - "none_same_size_as_text": "None (Same size as text)", - "small": "Small", - "normal": "Normal", - "large": "Large", - "extra_large": "Extra Large", - "language_specific_pronouns": "Language Specific Pronouns", - "show_pronouns_only_in_selected_language": "Show pronouns only in selected language", - "if_enabled_pronouns_are_only_shown_when_they_match_your_selected_language_t": "If enabled, pronouns are only shown when they match your selected language. This helps if your contacts set pronouns in different languages. It doesn't affect how your pronouns are shared with others.", - "selected_language_for_pronouns": "Selected language for pronouns", - "the_language_to_show_pronouns_for_when_the_above_setting_is_enabled": "The language to show pronouns for when the above setting is enabled.", - "language_code_e_g_en_de_en_de": "Language code (e.g. 'en', 'de', 'en,de')" - }, - "General": { - "formatting": "Formatting", - "month": "Month", - "day_of_the_week": "Day of the Week", - "day_of_the_week_sunday_0": "Day of the week (Sunday = 0)", - "two_letter_day_name": "Two-letter day name", - "short_day_name": "Short day name", - "full_day_name": "Full day name", - "day_of_the_month": "Day of the Month", - "full_month_name": "Full month name", - "short_month_name": "Short month name", - "the_month": "The month", - "two_digit_month": "Two-digit month", - "four_digit_year": "Four-digit year", - "two_digit_year": "Two-digit year" - } - }, - "RoomView": { - "failed_to_load_history": "Failed to load history.", - "failed_to_load_messages": "Failed to load messages.", - "jump_to_unread": "Jump to Unread", - "mark_as_read": "Mark as Read", - "new_messages": "New Messages", - "jump_to_latest": "Jump to Latest", - "call_started_by": "Call started by", - "start_voice_call": "Start Voice Call", - "typing": { - "are_typing": " are typing...", - "typing_sep_word": " and " - }, - "is_typing": "is typing...", - "close_threads": "Close threads", - "search_threads": "Search threads...", - "clear_search": "Clear search", - "Threads": { - "no_threads_match_your_search": "No threads match your search.", - "no_threads_yet": "No threads yet.", - "jump": "Jump", - "threads": "Threads", - "thread": "Thread", - "no_replies_yet_start_the_thread_below": "No replies yet. Start the thread below!" - }, - "join_new_room": "Join New Room", - "this_room_has_been_replaced_and_is_no_longer_active": "This room has been replaced and is no longer active.", - "failed_to_join_replacement_room": "Failed to join replacement room!", - "open_new_room": "Open New Room", - "scroll_to_top": "Scroll to Top", - "Message": { - "pin_message": "Pin Message", - "unpin_message": "Unpin Message", - "forwarded_private_message": "Forwarded private message", - "forwarded_from_earlier_in_this_room": "Forwarded from earlier in this room", - "forwarded_from_another_room": "Forwarded from another room", - "jump_to_original": "jump to original", - "failed_to_send": "Failed to send.", - "only_you_can_see_this": "Only you can see this.", - "edit_message": "Edit Message", - "Editor": { - "edit_message": "Edit message..." - } - }, - "Reactions": { - "reacted_with": "Reacted with" - } - }, - "RoomInput": { - "recording_duration": "Recording duration:", - "EmojiBoard": { - "no_results_found": "No Results found", - "search_results": "Search Results", - "personal_pack": "Personal Pack", - "unknown_pack": "Unknown Pack" - } - }, - "Room": { - "DirectInvite": {} - }, - "DevTools": { - "json_content": "JSON Content", - "account_data": "Account Data", - "developer_tools": "Developer Tools" } } diff --git a/public/locales/en/room/drawers/members.json b/public/locales/en/room/drawers/members.json new file mode 100644 index 0000000000..5930eddb32 --- /dev/null +++ b/public/locales/en/room/drawers/members.json @@ -0,0 +1,5 @@ +{ + "scroll_to_top": "Scroll to Top", + "no_members": "No '{{filter}}' Members", + "type_name": "Type name..." +} diff --git a/public/locales/en/room/drawers/reactions.json b/public/locales/en/room/drawers/reactions.json new file mode 100644 index 0000000000..1e4fe329b1 --- /dev/null +++ b/public/locales/en/room/drawers/reactions.json @@ -0,0 +1,3 @@ +{ + "reacted_with": "Reacted with" +} diff --git a/public/locales/en/room/drawers/threads.json b/public/locales/en/room/drawers/threads.json new file mode 100644 index 0000000000..030e333a3a --- /dev/null +++ b/public/locales/en/room/drawers/threads.json @@ -0,0 +1,13 @@ +{ + "close_threads": "Close threads", + "search_threads": "Search threads...", + "clear_search": "Clear search", + "no_threads_match_your_search": "No threads match your search.", + "no_threads_yet": "No threads yet.", + "jump": "Jump", + "threads": "Threads", + "thread": "Thread", + "no_replies_yet_start_the_thread_below": "No replies yet. Start the thread below!", + "reply_one": " reply", + "reply_other": " replies" +} diff --git a/public/locales/en/room/input.json b/public/locales/en/room/input.json new file mode 100644 index 0000000000..e65f0f05ff --- /dev/null +++ b/public/locales/en/room/input.json @@ -0,0 +1,9 @@ +{ + "recording_duration": "Recording duration:", + "EmojiBoard": { + "no_results_found": "No Results found", + "search_results": "Search Results", + "personal_pack": "Personal Pack", + "unknown_pack": "Unknown Pack" + } +} diff --git a/public/locales/en/room/room-view/replaced-room.json b/public/locales/en/room/room-view/replaced-room.json new file mode 100644 index 0000000000..a74d59460e --- /dev/null +++ b/public/locales/en/room/room-view/replaced-room.json @@ -0,0 +1,6 @@ +{ + "join_new_room": "Join New Room", + "this_room_has_been_replaced_and_is_no_longer_active": "This room has been replaced and is no longer active.", + "failed_to_join_replacement_room": "Failed to join replacement room!", + "open_new_room": "Open New Room" +} diff --git a/public/locales/en/room/room-view/room-buttons.json b/public/locales/en/room/room-view/room-buttons.json new file mode 100644 index 0000000000..cee6c2ef64 --- /dev/null +++ b/public/locales/en/room/room-view/room-buttons.json @@ -0,0 +1,3 @@ +{ + "start_voice_call": "Start Voice Call" +} diff --git a/public/locales/en/room/room-view/timeline-pills.json b/public/locales/en/room/room-view/timeline-pills.json new file mode 100644 index 0000000000..66ebb5c201 --- /dev/null +++ b/public/locales/en/room/room-view/timeline-pills.json @@ -0,0 +1,7 @@ +{ + "failed_to_load_history": "Failed to load history.", + "failed_to_load_messages": "Failed to load messages.", + "jump_to_unread": "Jump to Unread", + "mark_as_read": "Mark as Read", + "jump_to_latest": "Jump to Latest" +} diff --git a/public/locales/en/room/room-view/typing.json b/public/locales/en/room/room-view/typing.json new file mode 100644 index 0000000000..b0ef34c9a6 --- /dev/null +++ b/public/locales/en/room/room-view/typing.json @@ -0,0 +1,7 @@ +{ + "are_typing": " are typing...", + "typing_comma": ", ", + "typing_sep_word": " and ", + "is_typing": " is typing...", + "typing_others": " others" +} diff --git a/public/locales/en/settings/appearance.json b/public/locales/en/settings/appearance.json new file mode 100644 index 0000000000..d3da4e010b --- /dev/null +++ b/public/locales/en/settings/appearance.json @@ -0,0 +1,49 @@ +{ + "light_and_dark": "Light & dark", + "light_only": "Light only", + "dark_only": "Dark only", + "off": "Off", + "jumbo_emoji": "Jumbo Emoji", + "jumbo_emoji_size": "Jumbo Emoji Size", + "adjust_the_size_of_emojis_sent_without_text": "Adjust the size of emojis sent without text.", + "privacy_and_security": "Privacy & Security", + "blur_media": "Blur Media", + "blurs_images_and_videos_in_the_timeline": "Blurs images and videos in the timeline.", + "blur_avatars": "Blur Avatars", + "blurs_user_profile_pictures_and_room_icons": "Blurs user profile pictures and room icons.", + "blur_emotes": "Blur Emotes", + "blurs_emoticons_within_messages": "Blurs emoticons within messages.", + "identity": "Identity", + "colorful_names": "Colorful Names", + "assign_unique_colors_to_users_based_on_their_id_does_not_override_room_spac": "Assign unique colors to users based on their ID. Does not override room/space custom colors. Will override default role colors.", + "show_pronoun_pills": "Show Pronoun Pills", + "display_user_pronouns_in_the_message_timeline": "Display user pronouns in the message timeline.", + "max_pronoun_pills": "Max Pronoun Pills", + "maximum_number_of_pronoun_pills": "Maximum number of pronoun pills shown per user in the timeline. Additional pronouns appear behind the ... pill.", + "max_pronoun_pill_length": "Max Pronoun Pill Length", + "maximum_pronoun_pill_length": "Maximum characters shown in each pronoun pill before truncation.", + "pronoun_pills_for_all": "Pronoun Pills for All", + "attempts_to_convert_pronouns_in_names_into_pills_e_g_they_them_or_it_its_tu": "Attempts to convert pronouns in names into pills (e.g. [they/them] or (it/its) turns into a pill).", + "render_custom_profile_cards": "Render Custom Profile Cards", + "choose_whose_profile_card_colors_to_show_everyone_with_a_scheme_only_light": "Choose whose profile card colors to show: everyone with a scheme, only light or dark schemes, or hide them.", + "render_global_username_colors": "Render Global Username Colors", + "display_the_username_colors_anyone_can_set_in_their_account_settings": "Display the username colors anyone can set in their account settings.", + "render_space_room_username_colors": "Render Space/Room Username Colors", + "display_the_username_colors_that_can_be_set_with_color": "Display the username colors that can be set with /color.", + "render_space_room_fonts": "Render Space/Room Fonts", + "display_the_username_fonts_that_can_be_set_with_font": "Display the username fonts that can be set with /font.", + "consistent_icon_style": "Consistent Icon Style", + "harmonize_icon_appearance_with_background_fill": "Harmonize icon appearance with background fill", + "extra_small": "Extra Small", + "none_same_size_as_text": "None (Same size as text)", + "small": "Small", + "normal": "Normal", + "large": "Large", + "extra_large": "Extra Large", + "language_specific_pronouns": "Language Specific Pronouns", + "show_pronouns_only_in_selected_language": "Show pronouns only in selected language", + "if_enabled_pronouns_are_only_shown_when_they_match_your_selected_language_t": "If enabled, pronouns are only shown when they match your selected language. This helps if your contacts set pronouns in different languages. It doesn't affect how your pronouns are shared with others.", + "selected_language_for_pronouns": "Selected language for pronouns", + "the_language_to_show_pronouns_for_when_the_above_setting_is_enabled": "The language to show pronouns for when the above setting is enabled.", + "language_code_e_g_en_de_en_de": "Language code (e.g. 'en', 'de', 'en,de')" +} diff --git a/public/locales/en/settings/dev_tools.json b/public/locales/en/settings/dev_tools.json new file mode 100644 index 0000000000..bbd680faf8 --- /dev/null +++ b/public/locales/en/settings/dev_tools.json @@ -0,0 +1,5 @@ +{ + "json_content": "JSON Content", + "account_data": "Account Data", + "developer_tools": "Developer Tools" +} diff --git a/public/locales/en/settings/general.json b/public/locales/en/settings/general.json new file mode 100644 index 0000000000..59a8c6034f --- /dev/null +++ b/public/locales/en/settings/general.json @@ -0,0 +1,187 @@ +{ + "formatting": "Formatting", + "year": "Year", + "month": "Month", + "day_of_the_week": "Day of the Week", + "day_of_the_week_sunday_0": "Day of the week (Sunday = 0)", + "two_letter_day_name": "Two-letter day name", + "short_day_name": "Short day name", + "full_day_name": "Full day name", + "day_of_the_month": "Day of the Month", + "full_month_name": "Full month name", + "short_month_name": "Short month name", + "the_month": "The month", + "two_digit_month": "Two-digit month", + "four_digit_year": "Four-digit year", + "two_digit_year": "Two-digit year", + "two_digit_day_of_the_month": "Two-digit day of the month", + + "date_format": "Date Format", + "twenty_four_hour_time_format": "24-Hour Time Format", + "current_language": "Current Language", + + "Editor": { + "editor": "Editor", + "enter_for_newline_title": "ENTER for Newline", + "enter_for_newline_description": "Use {{keycombo}} + ENTER to send message.", + "composer_formatting_toolbar_title": "Message Formatting Toolbar", + "composer_formatting_toolbar_description": "Enable the formatting toolbar in the message composer.", + "hide_add_menu_title": "Hide Add Menu in the Editor", + "hide_add_menu_description": "Make the Plus button in the editor only add files. You may still send the special items using commands such as /poll and /location", + "hide_typing_indicators_title": "Hide Typing Indicators", + "hide_typing_indicators_description": "Turn off typing status.", + "hide_read_receipts_title": "Hide Read Receipts", + "hide_read_receipts_description": "Turn off read receipts.", + "presence_status_title": "Presence Status", + "presence_status_description": "Show and receive online status from other users.", + "reply_notifications_title": "Send notifications for replies", + "reply_notifications_description": "Disable to use silent replies by default. You can still toggle reply notifications for each reply.", + "individual_attachments_title": "Send text with individual attachment as caption", + "individual_attachments_description": "Send the contents of the message field as the attachment caption if present." + }, + "Gestures": { + "gestures": "Gestures", + "mobile_only": " (Mobile only)", + "enable_swiping_title": "Enable Swiping", + "enable_swiping_description": "Swipe left for rooms, swipe right for actions.", + "right_swipe_action_title": "Right Swipe Action", + "right_swipe_action_description": "What happens when you swipe right on a message." + }, + "Calls": { + "calls": "Calls", + "large_room_call_button_title": "Show Call Button for Large Rooms", + "join_on_click_voicecalls_title": "Join voice calls by just clicking the room's icon", + "incoming_call_sound_title": "Incoming Call Sound", + "incoming_call_sound_description": "Play ringtone audio for incoming calls.", + "notify_voice_rooms_title": "Notify For Voice Rooms", + "notify_voice_rooms_description": "Play ringtone audio when someone starts or joins a voice room.", + "outgoing_ringback_sound_title": "Outgoing Ringback Sound", + "outgoing_ringback_sound_description": "Play ringback while waiting for someone to join.", + "call_ringtone_title": "Ringtone", + "call_ringtone_description": "Choose the incoming call ringtone.", + "call_ringback_tone_title": "Ringback Tone", + "call_ringback_tone_description": "Choose what plays while your outgoing call is waiting.", + "call_ringtone_volume_title": "Ringtone Volume", + "always_play_call_sound_title": "Always Play Call Sound", + "always_play_call_sound_description": "Play call sounds even when message notification sounds are turned off.", + "custom_call_ringtone_title": "Custom Ringtone", + "custom_call_ringtone_description": "Import an audio file for your ringtone.", + "custom_call_ringtone_empty": "No custom ringtone imported.", + "custom_call_ringtone_preview": "Preview Ringtone", + "custom_call_ringback_title": "Custom Ringback", + "custom_call_ringback_description": "Import an audio file for outgoing ringback.", + "custom_call_ringback_empty": "No custom ringback imported.", + "custom_call_ringback_preview": "Preview Ringback", + "custom_ringtone_not_available": "Custom ringtone is not available on this device. Falling back to default.", + "custom_ringback_not_available": "Custom ringback is not available on this device. Falling back to default.", + "custom_file_imported": "Custom File (Imported)", + "custom_file": "Custom File", + "unable_to_preview_this_ringtone": "Unable to preview this ringtone in your browser.", + "could_not_import_file_format": "Could not import this file. Try a different audio format.", + "import_custom_ringtone_file": "Import a custom ringtone file first.", + "import_custom_ringback_file": "Import a custom ringback file first.", + "max_file_size": "Max file size: {{maxSize}}. ", + "max_file_duration": "Max duration: {{maxDuration}}.", + "only_audio_files_supported": "Only audio files are supported.", + "file_too_large": "File is too large. Max {{maxSize}} allowed", + "file_too_long": "{{label}} must be between 1s and {{maxDuration}}" + }, + "Messages": { + "messages": "Messages", + "message_spacing_title": "Message Spacing", + "file_description_placement_title": "File description placement", + + "disable_to_hide_redacted_messages_entirely": "Disable to hide redacted messages entirely instead of showing a tombstone.", + "enable_to_show_tombstone_events_for_redacted": "Enable to show tombstone events for redacted messages instead of hiding them entirely.", + "emoji_selector_threshold_title": "Emoji Selector Character Threshold", + "right_aligned_bubbles_title": "Right Aligned Bubbles", + "right_aligned_bubbles_description": "While using bubble layout, have your bubbles right aligned.", + "disable_media_auto_load_title": "Disable Media Auto Load", + "hide_membership_change_title": "Hide Membership Change", + "hide_profile_change_title": "Hide Profile Change", + "hide_member_events_read_only_rooms_title": "Hide Member Events in Read-Only Rooms", + "hide_member_events_read_only_rooms_description": "Hide membership changes, reactions, and reaction redactions in read-only rooms such as announcement channels.", + "show_hidden_events_title": "Show Hidden Events", + "show_hidden_events_description": "Reveal additional timeline events that are normally filtered out.", + "show_hidden_events_disable": "Disable to hide hidden events", + "show_hidden_events_enable": "Enable to show hidden events, this will cause visual clutter in busy rooms.", + "hidden_event_edits_title": "Show Edit Events", + "hidden_event_edits_description": "Show message edits as separate timeline events with a link to the previous version.", + "show_redacted_message_tombstones_title": "Show Tombstones for Redacted Messages", + "show_redacted_message_tombstones_description": "Show a tombstone in place of redacted messages instead of hiding them entirely.", + "hidden_event_redaction_timeline_title": "Show Redaction Events", + "hidden_event_redaction_timeline_description": "Show when a message was redacted as a timeline event with a link to the original message.", + "hidden_event_reactions_title": "Show Reaction Events", + "hidden_event_reactions_description": "Show reactions as separate timeline events with a link to the target message.", + "hidden_event_reaction_tombstones_title": "Show Tombstones for Redacted Reactions", + "hidden_event_reaction_tombstones_description": "Show a tombstone in place of redacted reactions instead of hiding them entirely.", + "hidden_event_reaction_redaction_timeline_title": "Show Reaction Redaction Events", + "hidden_event_reaction_redaction_timeline_description": "Show when a reaction was removed as a timeline event with a link to the target message.", + "hidden_event_other_title": "Show Other Hidden Events", + "hidden_event_other_description": "Show generic state events and other unrecognized timeline events." + }, + "Embeds": { + "embeds": "Embeds", + "display_multiple_embeds_title": "Display Multiple Embeds", + "display_multiple_embeds_description": "Display the embeds of all the links. Turning it off makes it only show the embed of the 1st item", + "display_bundled_embeds_title": "Display Bundled Embeds", + "display_bundled_embeds_description": "Show embeds when provided by the message itself. The embeds may be fabricated or incorrect.", + "url_preview_title": "Server-side Embeds", + "url_preview_description": "Send the links from inside the messages to your homeserver to generate previews of the linked pages.", + "encrypted_room_url_preview_title": "Server-side Embeds in Encrypted Room", + "encrypted_room_url_preview_description": "Request server-side embeds in E2EE chats. This partially decreases secrecy by revealing sent links to your homeserver", + "client_side_embeds_title": "Client-side Embeds", + "client_side_embeds_description": "Attempt to preview supported urls (e.g. YouTube) on the client, without involving the homeserver. This will expose your IP Address to third party services.", + "client_side_embeds_disable": "Disable client-side embeds", + "client_side_embeds_enable": "Enable client-side embeds", + "encrypted_room_embeds_title": "Client-side Embeds in Encrypted Rooms", + "encrypted_room_embeds_disable": "Disable client-side embeds in encrypted rooms", + "encrypted_room_embeds_enable": "Enable client-side embeds in encrypted rooms", + "embed_youtube_links_title": "Embed YouTube Links", + "embed_youtube_links_disable": "Disable client-side Youtube video embeds", + "embed_youtube_links_enable": "Enable client-side Youtube video embeds", + "enable_gif_picker_title": "Enable Gif Picker", + "enable_gif_picker_description": "Enables the gif picker in the emoji board. This reduces Privacy because it makes requests to klipy.com whenever you search for a gif.", + "enable_gif_picker_disable": "Disable GIF picker", + "enable_gif_picker_enable": "Enable GIF picker", + "show_interactive_map_title": "Show Interactive maps", + "show_interactive_map_description": "Show an interactive map in messages. This reduces Privacy because it requests map data from OpenStreetMap.org whenever you need to load a uncached part of the maps.", + "show_interactive_map_disable": "Disable Interactive Map", + "show_interactive_map_enable": "Enable Interactive Map", + "show_interactive_map_enc_title": "Show Interactive maps in Encrypted Rooms", + "show_interactive_map_enc_description": "Show an interactive map in Encrypted rooms. This reduces Privacy because it requests map data from OpenStreetMap.org whenever you need to load a uncached part of the maps.", + "show_interactive_map_enc_disable": "Disable Interactive Map", + "show_interactive_map_enc_enable": "Enable Interactive Map" + }, + "Sync": { + "sync": "Sync", + "use_sliding_sync_title": "Use Sliding Sync", + "use_sliding_sync_enable": "Enable Sliding Sync for this current login/session. Requires server support and admin configuration.", + "use_sliding_sync_disable": "Unavailable: the server has disabled Sliding Sync in its config.", + "use_sliding_sync_documentation": " More info/Documentation.", + "use_sliding_sync_issues": " Known issues (Sable GitHub)." + }, + "SettingsSync": { + "settings_sync": "Settings Sync & Backup", + "sync_across_devices_title": "Sync across devices", + "sync_across_devices_description": "Store your settings in your Matrix account so they follow you to any Sable instance. Notification and zoom preferences are kept per-device.", + "sync_status_title": "Sync status", + "sync_across_devices_export": "Export Settings", + "sync_across_devices_import": "Import Settings", + "sync_across_devices_error": "Sync failed, will retry on next change", + "sync_across_devices_syncing": "Syncing…", + "sync_across_devices_uninitialized": "Not yet synced this session", + "sync_across_devices_existing": "Last synced at {{time}}", + "sync_across_devices_invalid": "Could not import, file was invalid or you cancelled." + }, + "DiagnosticsAndPriivacy": { + "diagnostics_and_priivacy": "Diagnostics & Privacy", + "error_reporting_title": "Error Reporting", + "error_reporting_send": "Send anonymous crash reports to help improve Sable. No messages, room names, or personal data are included.", + "error_reporting_unimplemented": "Error reporting is not configured for this build.", + "session_replay_title": "Session Replay", + "session_replay_description": "Allow recording of UI interactions to help debug errors. All text, media, and inputs are fully masked before sending.", + "privacy_policy": "Privacy Policy", + "please_refresh": "Please refresh the page for these settings to take effect." + } +} diff --git a/public/locales/ro/events.json b/public/locales/ro/events.json new file mode 100644 index 0000000000..b2d906d11e --- /dev/null +++ b/public/locales/ro/events.json @@ -0,0 +1,12 @@ +{ + "forwarded_private_message": "Mesaj privat redirectΜ¦ionat", + "forwarded_from_earlier_in_this_room": "RedirectΜ¦ionat dintr-un mesaj mai vechi din aceastaΜ† cameraΜ†", + "forwarded_from_another_room": "RedirectΜ¦ionat din altaΜ† cameraΜ†", + "jump_to_original": "du-te la original", + "failed_to_send": "Trimitere esΜ¦uataΜ†.", + "only_you_can_see_this": "Doar tu potΜ¦i vedea asta.", + "edit_message": "EditeazaΜ† mesajul", + "Editor": { + "edit_message": "EditeazaΜ† mesajul..." + } +} diff --git a/public/locales/ro/general.json b/public/locales/ro/general.json index d4d6fbca93..391394f869 100644 --- a/public/locales/ro/general.json +++ b/public/locales/ro/general.json @@ -29,138 +29,13 @@ "pronouns": "Pronume", "unknown": "Necunoscut", "edit": "EditeazaΜ†", + "result_one": " rezultat", + "result_other": " rezultate", + "jump": "Sari", + "import": "ImportaΜ†", "Organisms": { "RoomCommon": { "changed_room_name": " a schimbat numele camerei" } - }, - "Settings": { - "Cosmetics": { - "light_and_dark": "Luminos & IΜ‚ntunecat", - "light_only": "Doar luminos", - "dark_only": "Doar iΜ‚ntunecat", - "off": "NiciodataΜ†", - "jumbo_emoji": "Emojiuri Jumbo", - "jumbo_emoji_size": "MaΜ†rime Emojiuri Jumbo", - "adjust_the_size_of_emojis_sent_without_text": "SchimbaΜ† maΜ†rimea emojiurilor trimise faΜ‚raΜ† text.", - "privacy_and_security": "ConfidenΘ›ialitate Θ™i Securitate", - "blur_media": "EstompazaΜ† media.", - "blurs_images_and_videos_in_the_timeline": "EstompeazaΜ† imaginile si videourile din mesaje.", - "blur_avatars": "EstompeazaΜ† avatar-uri", - "blurs_user_profile_pictures_and_room_icons": "EstompeazaΜ† imaginile de profil si iconitΜ¦ele camerelor.", - "blur_emotes": "EstompeazaΜ† emoticoane", - "blurs_emoticons_within_messages": "EstompeazaΜ† emoticoanele din mesaje.", - "identity": "Identitate", - "colorful_names": "Nume Colorate Aleatoriu", - "assign_unique_colors_to_users_based_on_their_id_does_not_override_room_spac": "Atribuie culori unici utilizatorilor pe baza ID-ului lor. AceastaΜ† setare nu va inlocuii culorile atribuite de cameraΜ†/spatΜ¦iu, dar va inlocuii culoarea rolului de bazaΜ†", - "show_pronoun_pills": "ArataΜ† pilule de pronume", - "display_user_pronouns_in_the_message_timeline": "Arata pronumele utilizatorilor in mesaje", - "max_pronoun_pills": "NumaΜ†r maxim de pilule de pronume", - "maximum_number_of_pronoun_pills": "NumaΜ†rul maxim de pilule de pronume prezente de persoanaΜ† in conversatie. Alte pronume apar dupaΜ† pilula intitulataΜ† '...'", - "max_pronoun_pill_length": "Lungimea maximaΜ† a pronumelor de profil", - "maximum_pronoun_pill_length": "NumaΜ†rul maxim de litere in fiecare pronume iΜ‚nainte de a fi trunchiataΜ†.", - "pronoun_pills_for_all": "Pilule de pronume pentru toataΜ† lumea", - "attempts_to_convert_pronouns_in_names_into_pills_e_g_they_them_or_it_its_tu": "IΜ‚ncearcaΜ† sa transformi pronumele din nume in pilule (ex. [el/lui] sau [ea/iei] este transformat in pilulaΜ†).", - "render_custom_profile_cards": "RedaΜ† carduri de profil personalizate.", - "choose_whose_profile_card_colors_to_show_everyone_with_a_scheme_only_light": "Alege caΜ‚nd sa se redea cardul de profil: pentru toataΜ† lumea, doar teme iΜ‚ntunecate, doar teme luminoase, sau nu le reda deloc.", - "render_global_username_colors": "RedaΜ† culorile generale numelor persoanelor", - "display_the_username_colors_anyone_can_set_in_their_account_settings": "RedaΜ† culorile numelor tuturor persoanelor care au fost setate de acestea.", - "render_space_room_username_colors": "RedaΜ† culorile numelor setate de CameraΜ†/SpatΜ¦iu.", - "display_the_username_colors_that_can_be_set_with_color": "RedaΜ† culorile numelor care pot fi setate cu /color.", - "render_space_room_fonts": "RedaΜ† fonturile Camerei/SpatΜ¦iului.", - "display_the_username_fonts_that_can_be_set_with_font": "RedaΜ† fontul numelor care pot fi setate cu /font.", - "consistent_icon_style": "Stil al icoanelor consistent.", - "harmonize_icon_appearance_with_background_fill": "ArmonizeazaΜ† forma icoanelor cu o culoare de fundal.", - "extra_small": "Foarte mic", - "none_same_size_as_text": "BazaΜ† (aceasΜ¦i maΜ†rime ca textul)", - "small": "Mic", - "normal": "Normal", - "large": "Mari", - "extra_large": "Foarte mari", - "language_specific_pronouns": "Pronume specifice limbii", - "show_pronouns_only_in_selected_language": "RedaΜ† doar pronumele in limba selectataΜ†", - "if_enabled_pronouns_are_only_shown_when_they_match_your_selected_language_t": "DacaΜ† activat, doar pronumele in limba selectataΜ† de tine vor apaΜ†rea. Asta ajuta dacaΜ† contactele tale au setata pronume in limbi diferite. Nu afecteaza cum pronumele setate de tine sunt iΜ‚mpaΜ†rtaΜ†sΜ¦ite.", - "selected_language_for_pronouns": "Limbi selectate pentru pronume.", - "the_language_to_show_pronouns_for_when_the_above_setting_is_enabled": "Limbile in care pronumele vor fi redate pentru setarea de asupra.", - "language_code_e_g_en_de_en_de": "Codul limbii (ex. 'ro', 'de', 'ro,de')" - }, - "General": { - "formatting": "Formatare", - "month": "Luna", - "day_of_the_week": "Ziua saΜ†ptaΜ†maΜ†nii", - "day_of_the_week_sunday_0": "Ziua saΜ†ptaΜ†maΜ†nii (DuminicaΜ† = 0)", - "two_letter_day_name": "Numele zilei cu 2 litere", - "short_day_name": "Numele zilei scurt", - "full_day_name": "Numele zilei iΜ‚ntreg", - "day_of_the_month": "Ziua din lunaΜ†", - "full_month_name": "Numele iΜ‚ntreg al lunii", - "short_month_name": "Numele scurt al lunii", - "the_month": "Luna", - "two_digit_month": "Luna cu 2 cifre", - "four_digit_year": "Anul cu 4 cifre", - "two_digit_year": "Anul cu 2 cifre" - } - }, - "RoomView": { - "failed_to_load_history": "IΜ‚ncaΜ†rcarea istoriei a esΜ¦uat.", - "failed_to_load_messages": "IΜ‚ncaΜ†rcarea mesajelor a esΜ¦uat.", - "jump_to_unread": "Sari la necitit", - "mark_as_read": "NoteazaΜ† ca Citit", - "new_messages": "Mesaje Noi", - "jump_to_latest": "Sari la ultimele mesaje", - "call_started_by": "Apel iΜ‚nceput de", - "start_voice_call": "IΜ‚ncepe apel audio", - "typing": { - "are_typing": " scriu...", - "typing_sep_word": " sΜ¦i " - }, - "is_typing": "scrie...", - "close_threads": "IΜ‚nchide fire", - "search_threads": "CautaΜ† fire...", - "clear_search": "CuraΜ†tΜ¦aΜ† caΜ†utarea", - "Threads": { - "no_threads_match_your_search": "Nici un fir nu se potrivesΜ¦te cautaΜ†rii.", - "no_threads_yet": "IΜ‚nca nu sunt fire.", - "jump": "Sari", - "threads": "Fire", - "thread": "Fir", - "no_replies_yet_start_the_thread_below": "IΜ‚ncaΜ† nu are raΜ†spunsuri. IΜ‚ncepe un fir de mesaje!" - }, - "join_new_room": "AlaΜ†turaΜ†te unei camere noi", - "this_room_has_been_replaced_and_is_no_longer_active": "AceasaΜ† camera a fost schimbataΜ† sΜ¦i nu mai este activaΜ†.", - "failed_to_join_replacement_room": "IΜ‚ncercarea de a te alaΜ†tura camerei noi a esΜ¦uat!", - "open_new_room": "Deschide CameraΜ† nouaΜ†", - "scroll_to_top": "DeruleazaΜ† iΜ‚n Sus", - "Message": { - "pin_message": "FixeazaΜ† Mesajul", - "unpin_message": "Scoata fixarea Mesajului", - "forwarded_private_message": "Mesaj privat redirectΜ¦ionat", - "forwarded_from_earlier_in_this_room": "RedirectΜ¦ionat dintr-un mesaj mai vechi din aceastaΜ† cameraΜ†", - "forwarded_from_another_room": "RedirectΜ¦ionat din altaΜ† cameraΜ†", - "jump_to_original": "du-te la original", - "failed_to_send": "Trimitere esΜ¦uataΜ†.", - "only_you_can_see_this": "Doar tu potΜ¦i vedea asta.", - "edit_message": "EditeazaΜ† mesajul", - "Editor": { - "edit_message": "EditeazaΜ† mesajul..." - } - }, - "Reactions": { - "reacted_with": "ReactΜ¦ionat cu" - } - }, - "RoomInput": { - "recording_duration": "Lungime iΜ‚nregistrare:", - "EmojiBoard": { - "no_results_found": "Nu un rezultat", - "search_results": "Rezultate caΜ†utare", - "personal_pack": "Pachet Personal", - "unknown_pack": "Pachet Necunoscut" - } - }, - "DevTools": { - "json_content": "ContΜ¦inut JSON", - "account_data": "Datele contului", - "developer_tools": "Unelete Dezvoltatori" } } diff --git a/public/locales/ro/room/create.json b/public/locales/ro/room/create.json index 5f0c32455d..2c0095dbe4 100644 --- a/public/locales/ro/room/create.json +++ b/public/locales/ro/room/create.json @@ -1,19 +1,19 @@ { "versions": "Versiuni", "founders": "Fondatori", - "special_privileged_users_can_be_assigned_during_creation_these_users_have_e": "In durata creaΜ†ri, anumite persoane pot fi privilegiate. Aceste persoana au un control ridicat si pot fi modificate doar cu o modernizare a camerei.", + "special_privileged_users_can_be_assigned_during_creation_these_users_have_e": "PotΜ¦i tribuii permissiuni speciale pe durata creatΜ¦iei. Aceste persoane vor avea un control ridicat. Lista de persoane speciale poate fi schimbataΜ† doar iΜ‚ntr-un 'upgrade'", "no_suggestions": "Nici o sugestie", - "please_provide_the_user_id_and_hit_enter": "Oferiti ID-ul de utilizator si apaΜ†satΜ¦i Enter.", - "only_member_of_parent_space_can_join": "Doar membrii a spatΜ¦iului paΜ†rinte se pot alaΜ†tura.", - "private": "PrivataΜ†", - "only_people_with_invite_can_join": "Doar persoanele cu o invitatΜ¦ie se pot alaΜ†tura.", - "anyone_with_the_address_can_join": "Oricine are adresa se poate alaΜ†tura.", - "public": "PublicaΜ†", - "address_optional": "AdresaΜ† (OptΜ¦ionalaΜ†)", - "pick_an_unique_address_to_make_it_discoverable": "Alege o adresaΜ† unicaΜ† saΜ† poataΜ† fi comunitatea descoperitaΜ†.", - "this_address_is_already_taken_please_select_a_different_one": "AceastaΜ† adresaΜ† este deja luataΜ†. SelectatΜ¦i una diferitaΜ†.", + "please_provide_the_user_id_and_hit_enter": "OferitΜ¦i ID-ul persoanel sΜ¦i apaΜ†satΜ¦i Enter.", + "only_member_of_parent_space_can_join": "Doar membrii a spatiului 'paΜ†rinte' se pot alaΜ†tura.", + "private": "Privat", + "only_people_with_invite_can_join": "Doar persoanele invitate se pot alaΜ†tura.", + "anyone_with_the_address_can_join": "Oricine se poate alaΜ†tura cu adresa.", + "public": "Public", + "address_optional": "AddresaΜ† (OptionalaΜ†)", + "pick_an_unique_address_to_make_it_discoverable": "Alege o adresaΜ† unicat saΜ† fie usΜ¦or de descoperit de alte persoane.", + "this_address_is_already_taken_please_select_a_different_one": "AceastaΜ† adresaΜ† este deja luataΜ†. Alege altaΜ† adresaΜ†.", "voice_room": "CameraΜ† vocalaΜ†", - "live_audio_and_video_conversations": "; conversatΜ¦ii audio-video live.", + "live_audio_and_video_conversations": "- Conversatii audio-video live.", "chat_room": "CameraΜ† discutΜ¦ii", - "messages_photos_and_videos": "; Mesaje, fotografii, and videoclipuri." + "messages_photos_and_videos": "- Mesaje, fotografii sΜ¦i videoclipuri." } diff --git a/public/locales/ro/room/direct/invite.json b/public/locales/ro/room/direct/invite.json index cf7ade5019..afd0ed9d24 100644 --- a/public/locales/ro/room/direct/invite.json +++ b/public/locales/ro/room/direct/invite.json @@ -1,7 +1,7 @@ { - "direct_message_invite_prompt": "Aceasa este o conversatΜ¦ie directaΜ†, menitaΜ† pentru discutΜ¦ii iΜ‚ntre douaΜ† persoane, DoritΜ¦i sa o tranformatΜ¦i intr-un group de conversatΜ¦ie iΜ‚nainte de a continua?", - "failed_to_convert_direct_message_to_room": "Transformarea convesatΜ¦iei directe in conversatΜ¦ie de grup a esΜ¦uat!", - "convert_to_group_chat_and_invite": "TransformaΜ† in grup sΜ¦i invitaΜ†", - "invite_to_direct_message_anyway": "InvitaΜ† la mesaje directe oricum", - "invite_another_member": "InvitaΜ† altaΜ† persoana" + "direct_message_invite_prompt": "Aceasta este o cameraΜ† directaΜ†, mentitaΜ† pentru o conversatΜ¦ie iΜ‚ntre douaΜ† persoane. DoresΜ¦ti saΜ† o transformi iΜ‚ntr-o conversatΜ¦ie de grup iΜ‚nainte saΜ† continui?", + "failed_to_convert_direct_message_to_room": "Transformarea a esΜ¦uat!", + "convert_to_group_chat_and_invite": "TransformaΜ† in conversatΜ¦ie de grup sΜ¦i invitaΜ†", + "invite_to_direct_message_anyway": "InvitaΜ† iΜ‚n cameraΜ† directaΜ†", + "invite_another_member": "InvitaΜ† altaΜ† persoanaΜ†" } diff --git a/public/locales/ro/room/drawers/members.json b/public/locales/ro/room/drawers/members.json new file mode 100644 index 0000000000..a31bef4076 --- /dev/null +++ b/public/locales/ro/room/drawers/members.json @@ -0,0 +1,5 @@ +{ + "scroll_to_top": "RuleazaΜ† spre iΜ‚nceput", + "no_members": "Nici un membru '{{filter}}'", + "type_name": "Scrie nume..." +} diff --git a/public/locales/ro/room/drawers/reactions.json b/public/locales/ro/room/drawers/reactions.json new file mode 100644 index 0000000000..8bd704d83e --- /dev/null +++ b/public/locales/ro/room/drawers/reactions.json @@ -0,0 +1,3 @@ +{ + "reacted_with": "A reactΜ¦ionat cu" +} diff --git a/public/locales/ro/room/drawers/threads.json b/public/locales/ro/room/drawers/threads.json new file mode 100644 index 0000000000..b2d6a1aca3 --- /dev/null +++ b/public/locales/ro/room/drawers/threads.json @@ -0,0 +1,14 @@ +{ + "close_threads": "IΜ‚nchide fire", + "search_threads": "CautaΜ† fir...", + "clear_search": "CuraΜ†tΜ¦aΜ† caΜ†utarea", + "no_threads_match_your_search": "Nu se potrivesΜ¦te nici un fir.", + "no_threads_yet": "IΜ‚ncaΜ† nu existaΜ† nici un fir.", + "jump": "Sari", + "threads": "Fire", + "thread": "Fir", + "no_replies_yet_start_the_thread_below": "Nici un raΜ†spuns iΜ‚ncaΜ†. IΜ‚ncepe un fir!", + "reply_one": " rΔƒspuns", + "reply_few": " rΔƒspunsuri", + "reply_other": " de rΔƒspunsuri" +} diff --git a/public/locales/ro/room/input.json b/public/locales/ro/room/input.json new file mode 100644 index 0000000000..254b580345 --- /dev/null +++ b/public/locales/ro/room/input.json @@ -0,0 +1,9 @@ +{ + "recording_duration": "Durata iΜ‚nregistraΜ†rii:", + "EmojiBoard": { + "no_results_found": "Nici un rezultat", + "search_results": "CautaΜ† rezultate", + "personal_pack": "Pachet personal", + "unknown_pack": "Pachet necunoscut" + } +} diff --git a/public/locales/ro/room/room-view/replaced-room.json b/public/locales/ro/room/room-view/replaced-room.json new file mode 100644 index 0000000000..8b49732092 --- /dev/null +++ b/public/locales/ro/room/room-view/replaced-room.json @@ -0,0 +1,6 @@ +{ + "join_new_room": "AlaΜ†turate camerei noi", + "this_room_has_been_replaced_and_is_no_longer_active": "AceastaΜ† cameraΜ† a fost schimbata sΜ¦i nu mai este activaΜ†.", + "failed_to_join_replacement_room": "AlaΜ†turarea la noua cameraΜ† a esΜ¦uat!", + "open_new_room": "Deschide camera nouaΜ†" +} diff --git a/public/locales/ro/room/room-view/room-buttons.json b/public/locales/ro/room/room-view/room-buttons.json new file mode 100644 index 0000000000..4ba0d8ff1b --- /dev/null +++ b/public/locales/ro/room/room-view/room-buttons.json @@ -0,0 +1,3 @@ +{ + "start_voice_call": "IΜ‚ncepe apel vocal" +} diff --git a/public/locales/ro/room/room-view/timeline-pills.json b/public/locales/ro/room/room-view/timeline-pills.json new file mode 100644 index 0000000000..5add633e53 --- /dev/null +++ b/public/locales/ro/room/room-view/timeline-pills.json @@ -0,0 +1,7 @@ +{ + "failed_to_load_history": "IΜ‚ncaΜ†rcarea istoriei a esΜ¦uat.", + "failed_to_load_messages": "IΜ‚ncaΜ†rcarea mesajelor a esΜ¦uat.", + "jump_to_unread": "RuleazaΜ† la Necitite", + "mark_as_read": "NoteazaΜ† ca citite", + "jump_to_latest": "RuleazaΜ† la Noi" +} diff --git a/public/locales/ro/room/room-view/typing.json b/public/locales/ro/room/room-view/typing.json new file mode 100644 index 0000000000..b33b29fc00 --- /dev/null +++ b/public/locales/ro/room/room-view/typing.json @@ -0,0 +1,7 @@ +{ + "are_typing": " sciu...", + "typing_comma": ", ", + "typing_sep_word": " sΜ¦i ", + "is_typing": " scrie...", + "typing_others": " altΜ¦ii" +} diff --git a/public/locales/ro/settings/appearance.json b/public/locales/ro/settings/appearance.json new file mode 100644 index 0000000000..33917ed0c6 --- /dev/null +++ b/public/locales/ro/settings/appearance.json @@ -0,0 +1,49 @@ +{ + "light_and_dark": "Luminos & IΜ‚ntunecat", + "light_only": "Doar luminos", + "dark_only": "Doar iΜ‚ntunecat", + "off": "NiciodataΜ†", + "jumbo_emoji": "Emojiuri Jumbo", + "jumbo_emoji_size": "MaΜ†rime Emojiuri Jumbo", + "adjust_the_size_of_emojis_sent_without_text": "SchimbaΜ† maΜ†rimea emojiurilor trimise faΜ‚raΜ† text.", + "privacy_and_security": "ConfidenΘ›ialitate Θ™i Securitate", + "blur_media": "EstompazaΜ† media.", + "blurs_images_and_videos_in_the_timeline": "BlureazaΜ† imaginile si videourile din mesaje.", + "blur_avatars": "BlureazaΜ† avatar-uri", + "blurs_user_profile_pictures_and_room_icons": "BlureazaΜ† imaginile de profil si iconitΜ¦ele camerelor.", + "blur_emotes": "BlureazaΜ† emoticoane", + "blurs_emoticons_within_messages": "BlureazaΜ† emoticoanele din mesaje.", + "identity": "Identitate", + "colorful_names": "Nume Colorate Aleatoriu", + "assign_unique_colors_to_users_based_on_their_id_does_not_override_room_spac": "Atribuie culori unici utilizatorilor pe baza ID-ului lor. AceastaΜ† setare nu va inlocuii culorile atribuite de cameraΜ†/spatΜ¦iu, dar va inlocuii culoarea rolului de bazaΜ†", + "show_pronoun_pills": "ArataΜ† pilule de pronume", + "display_user_pronouns_in_the_message_timeline": "Arata pronumele utilizatorilor in mesaje", + "max_pronoun_pills": "NumaΜ†r maxim de pilule de pronume", + "maximum_number_of_pronoun_pills": "NumaΜ†rul maxim de pilule de pronume prezente de persoanaΜ† in conversatie. Alte pronume apar dupaΜ† pilula intitulataΜ† '...'", + "max_pronoun_pill_length": "Lungimea maximaΜ† a pronumelor de profil", + "maximum_pronoun_pill_length": "NumaΜ†rul maxim de litere in fiecare pronume iΜ‚nainte de a fi trunchiataΜ†.", + "pronoun_pills_for_all": "Pilule de pronume pentru toataΜ† lumea", + "attempts_to_convert_pronouns_in_names_into_pills_e_g_they_them_or_it_its_tu": "IΜ‚ncearcaΜ† sa transformi pronumele din nume in pilule (ex. [el/lui] sau [ea/iei] este transformat in pilulaΜ†).", + "render_custom_profile_cards": "RedaΜ† carduri de profil personalizate.", + "choose_whose_profile_card_colors_to_show_everyone_with_a_scheme_only_light": "Alege caΜ‚nd sa se redea cardul de profil: pentru toataΜ† lumea, doar teme iΜ‚ntunecate, doar teme luminoase, sau nu le reda deloc.", + "render_global_username_colors": "RedaΜ† culorile generale numelor persoanelor", + "display_the_username_colors_anyone_can_set_in_their_account_settings": "RedaΜ† culorile numelor tuturor persoanelor care au fost setate de acestea.", + "render_space_room_username_colors": "RedaΜ† culorile numelor setate de CameraΜ†/SpatΜ¦iu.", + "display_the_username_colors_that_can_be_set_with_color": "RedaΜ† culorile numelor care pot fi setate cu /color.", + "render_space_room_fonts": "RedaΜ† fonturile Camerei/SpatΜ¦iului.", + "display_the_username_fonts_that_can_be_set_with_font": "RedaΜ† fontul numelor care pot fi setate cu /font.", + "consistent_icon_style": "Stil al icoanelor consistent.", + "harmonize_icon_appearance_with_background_fill": "ArmonizeazaΜ† forma icoanelor cu o culoare de fundal.", + "extra_small": "Foarte mic", + "none_same_size_as_text": "BazaΜ† (aceasΜ¦i maΜ†rime ca textul)", + "small": "Mic", + "normal": "Normal", + "large": "Mari", + "extra_large": "Foarte mari", + "language_specific_pronouns": "Pronume specifice limbii", + "show_pronouns_only_in_selected_language": "RedaΜ† doar pronumele in limba selectataΜ†", + "if_enabled_pronouns_are_only_shown_when_they_match_your_selected_language_t": "DacaΜ† activat, doar pronumele in limba selectataΜ† de tine vor apaΜ†rea. Asta ajuta dacaΜ† contactele tale au setata pronume in limbi diferite. Nu afecteaza cum pronumele setate de tine sunt iΜ‚mpaΜ†rtaΜ†sΜ¦ite.", + "selected_language_for_pronouns": "Limbi selectate pentru pronume.", + "the_language_to_show_pronouns_for_when_the_above_setting_is_enabled": "Limbile in care pronumele vor fi redate pentru setarea de asupra.", + "language_code_e_g_en_de_en_de": "Codul limbii (ex. 'ro', 'de', 'ro,de')" +} diff --git a/public/locales/ro/settings/dev_tools.json b/public/locales/ro/settings/dev_tools.json new file mode 100644 index 0000000000..9a9d6caae2 --- /dev/null +++ b/public/locales/ro/settings/dev_tools.json @@ -0,0 +1,5 @@ +{ + "json_content": "ContΜ¦inut JSON", + "account_data": "Datele contului", + "developer_tools": "Unelete Dezvoltatori" +} diff --git a/public/locales/ro/settings/general.json b/public/locales/ro/settings/general.json new file mode 100644 index 0000000000..027477abe6 --- /dev/null +++ b/public/locales/ro/settings/general.json @@ -0,0 +1,186 @@ +{ + "formatting": "Formatare", + "year": "An", + "month": "Luna", + "day_of_the_week": "Ziua saΜ†ptaΜ†maΜ†nii", + "day_of_the_week_sunday_0": "Ziua saΜ†ptaΜ†maΜ†nii (DuminicaΜ† = 0)", + "two_letter_day_name": "Numele zilei cu 2 litere", + "short_day_name": "Numele zilei scurt", + "full_day_name": "Numele zilei iΜ‚ntreg", + "day_of_the_month": "Ziua din lunaΜ†", + "full_month_name": "Numele iΜ‚ntreg al lunii", + "short_month_name": "Numele scurt al lunii", + "the_month": "Luna", + "two_digit_month": "Luna cu 2 cifre", + "four_digit_year": "Anul cu 4 cifre", + "two_digit_year": "Anul cu 2 cifre", + "two_digit_day_of_the_month": "Ziua din lunaΜ† cu 2 cifre month", + + "date_format": "Formatul zilei", + "twenty_four_hour_time_format": "Format in 24 de ore", + "current_language": "Limba folositaΜ†", + + "Editor": { + "editor": "Editor", + "enter_for_newline_title": "ENTER adaugaΜ† linie nouaΜ†", + "enter_for_newline_description": "FolosesΜ¦te {{keycombo}} + ENTER pentru a trimite un mesaj.", + "composer_formatting_toolbar_title": "BaraΜ† pentru modelarea mesajelor", + "composer_formatting_toolbar_description": "ActiveazaΜ† bara de modelare in zona de scriere a mesajului.", + "hide_add_menu_title": "Ascunde Meniul de adaΜ†ugare din editor", + "hide_add_menu_description": "Butonul de plus va putea doar saΜ† adauge file. VetΜ¦i putea trimite mesaje speciale folosind comenzi precum /poll sΜ¦i /location.", + "hide_typing_indicators_title": "Ascunde indicatorul de scriere", + "hide_typing_indicators_description": "Ascunde textul care descrie cine scrie activ.", + "hide_read_receipts_title": "Ascunde lista de citite", + "hide_read_receipts_description": "Ascunde lista de persoane care urmaΜ†resc conversatΜ¦ia activ.", + "presence_status_title": "PrezentΜ¦a activaΜ†", + "presence_status_description": "Timite si primeste status-ul de activiate de la alte persoane.", + "reply_notifications_title": "Trimite notificaΜ†ri in raΜ†spunsuri", + "reply_notifications_description": "DezactiveazaΜ† pentru a trimite raΜ†spunsuri silentΜ¦ioase automat. VetΜ¦i putea trimite notificaΜ†ri ne-silentΜ¦ioase prin apaΜ†sarea clopotΜ¦elului iΜ‚nainte de a trimite raΜ†spunsul.", + "individual_attachments_title": "Trimite text care contΜ¦ine un atasΜ¦ament ca pe o descriere.", + "individual_attachments_description": "Trimite continutul mesajului ca descriere dacaΜ† exista atasΜ¦amente." + }, + "Gestures": { + "gestures": "Gesturi", + "mobile_only": " (Doar pe mobil)", + "enable_swiping_title": "ActiveazaΜ† glisare", + "enable_swiping_description": "GliseazaΜ† spre dreapta pentru camere, sΜ¦i spre staΜ‚nga pentru actΜ¦iuni", + "right_swipe_action_title": "ActΜ¦iunea spre StaΜ‚nga", + "right_swipe_action_description": "Ce se iΜ‚ntaΜ†mplaΜ† caΜ‚nd glisatΜ¦i spre staΜ‚nga." + }, + "Calls": { + "calls": "Apeluri", + "large_room_call_button_title": "AfisΜ¦eazaΜ† butonul de apeluri iΜ‚n camerele largi", + "join_on_click_voicecalls_title": "ParticipaΜ† iΜ‚n apeluri de voce prin simpla apaΜ†sare a iconitΜ¦ei camerei", + "incoming_call_sound_title": "Tonul de apel primit", + "incoming_call_sound_description": "AscultaΜ† tonul de apel de intrare.", + "notify_voice_rooms_title": "NotificaΜ†ri de apel", + "notify_voice_rooms_description": "PornesΜ¦te sunetul de apel caΜ‚nd cineva iΜ‚ncepe sau se alaΜ†tura unui apel.", + "outgoing_ringback_sound_title": "Tonul de apel trimis", + "outgoing_ringback_sound_description": "PornesΜ¦te tonul de apel de iesΜ¦ire caΜ‚nd asΜ¦teptaΜ‚nd ca cineva saΜ† se alaΜ†ture.", + "call_ringtone_title": "Ton de intrare", + "call_ringtone_description": "Alege tonul de apel de intrare.", + "call_ringback_tone_title": "Ton de iesΜ¦ire", + "call_ringback_tone_description": "Alege tonul de apel de iesΜ¦ire.", + "call_ringtone_volume_title": "Volumul tonului", + "always_play_call_sound_title": "PornesΜ¦te sunetul de apel orice ar fii", + "always_play_call_sound_description": "PornesΜ¦te tonul de apel chiar dacaΜ† suneul de notificaΜ†ri sunt oprite.", + "custom_call_ringtone_title": "Ton de intrare personalizat", + "custom_call_ringtone_description": "AdaugaΜ† o fisΜ¦aΜ† audio pentru tonul taΜ†u.", + "custom_call_ringtone_empty": "Nici un ton personal nu a fost adaΜ†ugat.", + "custom_call_ringtone_preview": "Previzualizare Ton", + "custom_call_ringback_title": "Ton de iesΜ¦ire personal", + "custom_call_ringback_description": "AdaugaΜ† o fisΜ¦aΜ† audio pentru tonul de iesΜ¦ire.", + "custom_call_ringback_empty": "Nici un ton de iesΜ¦ire personal nu a fost adaΜ†ugat.", + "custom_call_ringback_preview": "Previzualizare Ton de iesΜ¦ire", + "custom_ringtone_not_available": "AdaΜ†ugarea de tonuri de intrare pestonalizate nu este posibilaΜ† pe acest dispozitiv. Revenim la setarea implicitaΜ†.", + "custom_ringback_not_available": "AdaΜ†ugarea de tonuri de iesΜ¦ire pestonalizate nu este posibilaΜ† pe acest dispozitiv. Revenim la setarea implicitaΜ†.", + "custom_file_imported": "FisΜ¦aΜ† personalaΜ† (adaΜ†ugataΜ†)", + "custom_file": "FisΜ¦aΜ† personalaΜ†", + "unable_to_preview_this_ringtone": "Previzualizarea acestui ton nu este posibilaΜ† pe acest dispozitiv.", + "could_not_import_file_format": "AdaΜ†ugarea acestei fisΜ¦e a esΜ¦uat. IΜ‚ncearca un alt format audio.", + "import_custom_ringtone_file": "AdaugaΜ† un ton de apel de intrare mai iΜ‚ntaΜ†i.", + "import_custom_ringback_file": "AdaugaΜ† un ton de apel de iesΜ¦ire mai iΜ‚ntaΜ†i.", + "max_file_size": "MaΜ†rimea maximaΜ†: {{maxSize}}. ", + "max_file_duration": "Durata maximaΜ†: {{maxDuration}}.", + "only_audio_files_supported": "Sunt permise doar formate audio.", + "file_too_large": "FisΜ¦a este prea mare. Maximul permis este de {{maxSize}}", + "file_too_long": "{{label}} trebuie saΜ† se iΜ‚ncadreze iΜ‚ntre 1s sΜ¦i {{maxDuration}}" + }, + "Messages": { + "messages": "Mesaje", + "message_spacing_title": "SpatΜ¦ierea mesajelor", + "file_description_placement_title": "PozitΜ¦ia descrierii fisΜ¦elor", + "disable_to_hide_redacted_messages_entirely": "DezactiveazaΜ† pentru a ascunde mesajele redactate cu totul iΜ‚n loc de a araΜ†ta o notitΜ¦aΜ†.", + "enable_to_show_tombstone_events_for_redacted": "ActiveazaΜ† pentru a afisΜ¦a o notitΜ¦aΜ† mentionaΜ†nd mesajul sΜ¦ters iΜ‚n loc ca acesta saΜ† fie ascuns complet.", + "emoji_selector_threshold_title": "Prag de caracter pentru slector de Emoji", + "right_aligned_bubbles_title": "Bule aliniate la dreapta", + "right_aligned_bubbles_description": "CaΜ‚nd folosesΜ¦ti afisΜ¦ajul de bule, mesajele tale vor fi aliniate la dreapta.", + "disable_media_auto_load_title": "DezactiveazaΜ† iΜ‚ncaΜ†rcarea automataΜ† a fisΜ¦elor media", + "hide_membership_change_title": "Ascunde schimbaΜ†rile de participare", + "hide_profile_change_title": "Ascunde schimbaΜ†rile in profil", + "hide_member_events_read_only_rooms_title": "Ascunde evenimentele de membru in camerele 'doar citire'", + "hide_member_events_read_only_rooms_description": "Ascunde schimbaΜ†rile de participare, reactΜ¦iile, si redactΜ¦ionarea reactΜ¦iilor in camere 'doar citire' precum camerele de anuntΜ¦uri.", + "show_hidden_events_title": "AfisΜ¦eazaΜ† evenimente ascunse", + "show_hidden_events_description": "DescoperaΜ† evenimente din conversatΜ¦ie care sunt de obicei asunse.", + "show_hidden_events_disable": "DezactiveazaΜ† pentru a nu afisΜ¦a evenimentele ascunse", + "show_hidden_events_enable": "ActiveazaΜ† pentru a afisΜ¦a evenimentele ascunse, asta va crea un sense in dezordine in camerele foarte active.", + "hidden_event_edits_title": "AfisΜ¦eazaΜ† evenimentele de editaj", + "hidden_event_edits_description": "AfisΜ¦eazaΜ† evenimentele de editare ca evenimente separate cu 'link' spre versiunea trecutaΜ†.", + "show_redacted_message_tombstones_title": "AfisΜ¦eazaΜ† notitΜ¦e pentru mesajele redactate", + "show_redacted_message_tombstones_description": "AfisΜ¦eazaΜ† o notitΜ¦a in locul mesajului redactat iΜ‚n loc de a fi complet ascuns complet.", + "hidden_event_redaction_timeline_title": "AfisΜ¦eazaΜ† evenimentele de redactare", + "hidden_event_redaction_timeline_description": "AfisΜ¦eazaΜ† redactarea unui mesaj cu un 'link' la mesajul care a fost redactat.", + "hidden_event_reactions_title": "AfisΜ¦eazaΜ† evenimentele de reactΜ¦ie", + "hidden_event_reactions_description": "AfisΜ¦eazaΜ† reactiile ca evenimente separate cu un 'link' spre mesajul spre care a fost directionataΜ† reactΜ¦ia.", + "hidden_event_reaction_tombstones_title": "AfisΜ¦eazaΜ† notitΜ¦e pentru reactΜ¦ii redactate", + "hidden_event_reaction_tombstones_description": "AfisΜ¦eazaΜ† o notitΜ¦aΜ† pentru redactarea reactΜ¦iei iΜ‚n loc de a le ascunde complet.", + "hidden_event_reaction_redaction_timeline_title": "AfisΜ¦eazaΜ† evenimente de redactΜ¦ie reactΜ¦iilor", + "hidden_event_reaction_redaction_timeline_description": "AfisΜ¦eazaΜ† evenimentul de redactare a unei reactΜ¦ii cu un 'link' spre mesasj iΜ‚n loc de a ascunde asta complet.", + "hidden_event_other_title": "AfisΜ¦eazaΜ† alte evenimete ascunse", + "hidden_event_other_description": "AfisΜ¦eazaΜ† evenimente generice de statut si alte evenimente necunoscute." + }, + "Embeds": { + "embeds": "IΜ‚ncorporaΜ†ri", + "display_multiple_embeds_title": "AfisΜ¦eazaΜ† iΜ‚ncorporaΜ†ri multiple", + "display_multiple_embeds_description": "AfisΜ¦eazaΜ† iΜ‚ncorporaΜ†ri pentru fiecare link iΜ‚n parte. Dezactivarea acestei setaΜ†ri va face aplicatΜ¦ia sa afisΜ¦eze iΜ‚ncorporare pentru primul link", + "display_bundled_embeds_title": "AfisΜ¦eazaΜ† iΜ‚ncorprari 'Bundled'", + "display_bundled_embeds_description": "AfisΜ¦eazaΜ† iΜ‚ncorporari furnizate de mesaj. Aceste iΜ‚ncorporari ar putea fi fabricate sau incorecte.", + "url_preview_title": "IΜ‚ncorporaΜ†ri de la server", + "url_preview_description": "Trimite link-urile din mesaje spre serverul taΜ†u pentru a genera iΜ‚ncorporaΜ†ri pentru link-uri.", + "encrypted_room_url_preview_title": "IΜ‚ncorporaΜ†ri de la server iΜ‚n camere criptate", + "encrypted_room_url_preview_description": "Cere iΜ‚ncorporaΜ†ri de la server pentru conversatii criptate end-to-end. Asta reduce confientΜ¦ialitatea partΜ¦ial iΜ‚ntrucaΜ†t necesitaΜ† trimiterea link-ului care este parte din mesaj spre server-ul taΜ†u", + "client_side_embeds_title": "IΜ‚ncorporaΜ†ri de la aplicatΜ¦ie", + "client_side_embeds_description": "IΜ‚ncearcaΜ† saΜ† creezi iΜ‚ncrporari pentru link-uri permise (ex. YouTube) de aplicatie, faΜ‚raΜ† saΜ† trimitaΜ† link-ul spre server. Asta iΜ‚tΜ¦i va expune adresa de IP spre servere 'third-party'.", + "client_side_embeds_disable": "DezactiveazaΜ† iΜ‚ncorporari de la aplicatΜ¦ie", + "client_side_embeds_enable": "ActiveazaΜ† iΜ‚ncorporari de la aplicatΜ¦ie", + "encrypted_room_embeds_title": "IΜ‚ncorporaΜ†ri de la apricatΜ¦ie iΜ‚n camere criptate", + "encrypted_room_embeds_disable": "DezactiveazaΜ† iΜ‚ncorporari de la aplicatΜ¦ie in camerele criptate", + "encrypted_room_embeds_enable": "ActiveazaΜ† iΜ‚ncorporari de la aplicatΜ¦ie in camerele criptate", + "embed_youtube_links_title": "IΜ‚ncorporaΜ†ri pentru link-uri Youtube", + "embed_youtube_links_disable": "Dezactiveaza iΜ‚ncorporarile create de aplicatΜ¦ie pentru videoclipuri youtube", + "embed_youtube_links_enable": "Activeaza iΜ‚ncorporarile create de aplicatΜ¦ie pentru videoclipuri youtube", + "enable_gif_picker_title": "ActiveazaΜ† selectorul de GIF-uri", + "enable_gif_picker_description": "ActiveazaΜ† selectorul de GIF-uri. Asta reduce confidentΜ¦ialitatea deoarece creazaΜ† request-uri spre klipy.com caΜ‚nd cautΜ¦i un gif.", + "enable_gif_picker_disable": "DezactiveazaΜ† selectorul de GIF-uri", + "enable_gif_picker_enable": "ActiveazaΜ† selectorul de GIF-uri", + "show_interactive_map_title": "AfisΜ¦eazaΜ† haΜ†rtΜ¦i interactive", + "show_interactive_map_description": "AfisΜ¦eazaΜ† haΜ†rtΜ¦i interactive iΜ‚n mesaje. Asta reduce confidentΜ¦ialitatea deoarece creazaΜ† request-uri spre OpenStreetMap.org pentru paΜ†rtΜ¦i din hartaΜ† care nu sunt salvate local.", + "show_interactive_map_disable": "DezactiveazaΜ† haΜ†rtΜ¦i interactive", + "show_interactive_map_enable": "ActiveazaΜ† haΜ†rtΜ¦i interactive", + "show_interactive_map_enc_title": "AfisΜ¦eazaΜ† haΜ†rtΜ¦i interactive iΜ‚n camerele criptate", + "show_interactive_map_enc_description": "AfisΜ¦eazaΜ† haΜ†rtΜ¦i interactive iΜ‚n camerle criptate. Asta reduce confidentΜ¦ialitatea deoarece creazaΜ† request-uri spre OpenStreetMap.org pentru paΜ†rtΜ¦i din hartaΜ† care nu sunt salvate local.", + "show_interactive_map_enc_disable": "DezactiveazaΜ† haΜ†rtΜ¦i interactive", + "show_interactive_map_enc_enable": "ActiveazaΜ† haΜ†rtΜ¦i interactive" + }, + "Sync": { + "sync": "Sincronizare", + "use_sliding_sync_title": "FolosesΜ¦te sincronizare 'Sliding'", + "use_sliding_sync_enable": "ActiveazaΜ† sincronizarea 'sliding' pentru sesiunea aceasta. NecesitaΜ† suport de la server.", + "use_sliding_sync_disable": "Neaccesibil: Server-ul a dezactivat sincronizarea 'sliding' in configurare.", + "use_sliding_sync_documentation": " Mai multaΜ† informatΜ¦ie/documentatΜ¦ie.", + "use_sliding_sync_issues": " Probleme Cunsocute (GitHub Sable)." + }, + "SettingsSync": { + "settings_sync": "Sincronizare setaΜ†ri si backup", + "sync_across_devices_title": "SincronizeazaΜ† iΜ‚ntre dispozitive", + "sync_across_devices_description": "SalveazaΜ† setaΜ†rile in contul taΜ†u matrix pentru ca ele saΜ† fie folosite pentru fiecare dispozitiv. NotificaΜ†rile si zoom-ul sunt pastrate individualizate dispozitivului respectiv.", + "sync_status_title": "SituatΜ¦ia sincronizaΜ†rii", + "sync_across_devices_export": "ExportaΜ† SetaΜ†ri", + "sync_across_devices_import": "ImportaΜ† SetaΜ†ri", + "sync_across_devices_error": "Sincronizarea a esΜ¦uat, vom iΜ‚ncerca din nou la urmaΜ†toarea schimbare", + "sync_across_devices_syncing": "SincronizaΜ‚nd…", + "sync_across_devices_uninitialized": "Aceasta sesiune nu a fost iΜ‚nca sincronizataΜ†", + "sync_across_devices_existing": "Ultima sincronizare a fost la {{time}}", + "sync_across_devices_invalid": "Importarea a esΜ¦uat, fisΜ¦a a fost incorectaΜ† sau procesul a fost anulat." + }, + "DiagnosticsAndPriivacy": { + "diagnostics_and_priivacy": "Diagnostice si ConfidentΜ¦ialitate", + "error_reporting_title": "Raporare Erori", + "error_reporting_send": "Trimite raporturi anonime dacaΜ† tΜ¦i se blocheazaΜ† aplicatΜ¦ia. Nici un mesaj, nume al camerei, sau informatΜ¦ie personala este inclusaΜ†.", + "error_reporting_unimplemented": "Raportarea de erori nu este configurata pe acest build.", + "session_replay_title": "Reluare a sesiunii", + "session_replay_description": "Permite iΜ‚nregistrarea interactΜ¦iunilor UI pentru a ajuta iΜ‚n rezolvarea problemelor. Tot textul, media, si scrisul este mascat inainte de a fi trimis.", + "privacy_policy": "PoliticaΜ† de ConfidentΜ¦ialitate", + "please_refresh": "ReiΜ‚mprospateazaΜ† aplicatΜ¦ia pentru ca aceste schimbaΜ†ri saΜ† iΜ‚sΜ¦i facaΜ† efectul." + } +} diff --git a/src/app/components/AccountDataEditor.tsx b/src/app/components/AccountDataEditor.tsx index f67baaba38..083c695a1c 100644 --- a/src/app/components/AccountDataEditor.tsx +++ b/src/app/components/AccountDataEditor.tsx @@ -23,7 +23,7 @@ import { useTextAreaCodeEditor } from '$hooks/useTextAreaCodeEditor'; import { Page, PageHeader } from './page'; import { SequenceCard } from './sequence-card'; import { TextViewerContent } from './text-viewer'; -import { t } from 'i18next'; +import { useTranslation } from 'react-i18next'; const EDITOR_INTENT_SPACE_COUNT = 2; @@ -49,6 +49,7 @@ function AccountDataEdit({ onSave, }: AccountDataEditProps) { const alive = useAlive(); + const { t } = useTranslation(['settings/dev_tools', 'general']); const textAreaRef = useRef(null); const [jsonError, setJSONError] = useState(); @@ -122,7 +123,7 @@ function AccountDataEdit({ aria-disabled={submitting} > - {t('DevTools.account_data')} + {t('account_data')} } > - {t('General.save')} + {t('save', { ns: 'general' })} @@ -166,7 +167,7 @@ function AccountDataEdit({ - {t('DevTools.json_content')} + {t('json_content')} void; }; function AccountDataView({ type, defaultContent, onEdit }: AccountDataViewProps) { + const { t } = useTranslation(['settings/dev_tools', 'general']); return ( - {t('DevTools.account_data')} + {t('account_data')} - {t('DevTools.json_content')} + {t('json_content')} ({ type: type ?? '', content: content ?? {}, @@ -291,7 +294,7 @@ export function AccountDataEditor({ onClick={requestClose} before={sizedIcon(ArrowLeft, '100')} > - {t('DevTools.developer_tools')} + {t('developer_tools')} diff --git a/src/app/components/create-room/CreateRoomAliasInput.tsx b/src/app/components/create-room/CreateRoomAliasInput.tsx index 97e68ae768..cb208b44d6 100644 --- a/src/app/components/create-room/CreateRoomAliasInput.tsx +++ b/src/app/components/create-room/CreateRoomAliasInput.tsx @@ -10,7 +10,7 @@ import type { AsyncState } from '$hooks/useAsyncCallback'; import { AsyncStatus, useAsync } from '$hooks/useAsyncCallback'; import { useDebounce } from '$hooks/useDebounce'; import { getMxIdServer } from '$utils/mxIdHelper'; -import { t } from 'i18next'; +import { useTranslation } from 'react-i18next'; export function CreateRoomAliasInput({ disabled }: { disabled?: boolean }) { const mx = useMatrixClient(); @@ -18,6 +18,7 @@ export function CreateRoomAliasInput({ disabled }: { disabled?: boolean }) { const [aliasAvail, setAliasAvail] = useState>({ status: AsyncStatus.Idle, }); + const { t } = useTranslation('room/create'); useEffect(() => { if (aliasAvail.status === AsyncStatus.Success && aliasInputRef.current?.value === '') { @@ -75,9 +76,9 @@ export function CreateRoomAliasInput({ disabled }: { disabled?: boolean }) { return ( - {t('RoomCreate.address_optional')} + {t('address_optional')} - {t('RoomCreate.pick_an_unique_address_to_make_it_discoverable')} + {t('pick_an_unique_address_to_make_it_discoverable')} {sizedIcon(Warning, '50', { filled: true })} - {t('RoomCreate.this_address_is_already_taken_please_select_a_different_one')} + {t('this_address_is_already_taken_please_select_a_different_one')} )} diff --git a/src/app/components/create-room/CreateRoomTypeSelector.tsx b/src/app/components/create-room/CreateRoomTypeSelector.tsx index 7ff9159f30..fe2407088e 100644 --- a/src/app/components/create-room/CreateRoomTypeSelector.tsx +++ b/src/app/components/create-room/CreateRoomTypeSelector.tsx @@ -5,7 +5,7 @@ import { SequenceCard } from '$components/sequence-card'; import { SettingTile } from '$components/setting-tile'; import { BetaNoticeBadge } from '$components/BetaNoticeBadge'; import { CreateRoomType } from './types'; -import { t } from 'i18next'; +import { useTranslation } from 'react-i18next'; type CreateRoomTypeSelectorProps = { value?: CreateRoomType; @@ -19,6 +19,7 @@ export function CreateRoomTypeSelector({ disabled, getIcon, }: CreateRoomTypeSelectorProps) { + const { t } = useTranslation('room/create'); return ( - {t('RoomCreate.chat_room')} + {t('chat_room')} - {t('RoomCreate.messages_photos_and_videos')} + {t('messages_photos_and_videos')} @@ -63,10 +64,10 @@ export function CreateRoomTypeSelector({ > - {t('RoomCreate.voice_room')} + {t('voice_room')} - {t('RoomCreate.live_audio_and_video_conversations')} + {t('live_audio_and_video_conversations')} diff --git a/src/app/components/emoji-board/EmojiBoard.tsx b/src/app/components/emoji-board/EmojiBoard.tsx index 82cd4cf179..de466250c0 100644 --- a/src/app/components/emoji-board/EmojiBoard.tsx +++ b/src/app/components/emoji-board/EmojiBoard.tsx @@ -59,9 +59,9 @@ import { } from './components'; import type { GifData } from './types'; import { EmojiBoardTab, EmojiType } from './types'; -import { t } from 'i18next'; import { useClientConfig } from '$hooks/useClientConfig'; import { useFavoriteGifs } from '$hooks/useFavoriteGifs'; +import { useTranslation } from 'react-i18next'; /* oxlint-disable typescript/no-explicit-any */ // TODO: type klipy api properly @@ -97,6 +97,7 @@ const useGroups = ( const recentEmojis = useRecentEmoji(mx, 21); const labels = useEmojiGroupLabels(); + const { t } = useTranslation(['room/input']); const emojiGroupItems = useMemo(() => { const g: EmojiGroupItem[] = []; @@ -111,9 +112,7 @@ const useGroups = ( imagePacks.forEach((pack) => { let label = pack.meta.name; if (!label) - label = isUserId(pack.id) - ? t('RoomInput.EmojiBoard.personal_pack') - : mx.getRoom(pack.id)?.name; + label = isUserId(pack.id) ? t('EmojiBoard.personal_pack') : mx.getRoom(pack.id)?.name; g.push({ id: pack.id, @@ -133,7 +132,7 @@ const useGroups = ( }); return g; - }, [mx, recentEmojis, labels, imagePacks, tab]); + }, [mx, recentEmojis, labels, imagePacks, tab, t]); const stickerGroupItems = useMemo(() => { const g: StickerGroupItem[] = []; @@ -142,9 +141,7 @@ const useGroups = ( imagePacks.forEach((pack) => { let label = pack.meta.name; if (!label) - label = isUserId(pack.id) - ? t('RoomInput.EmojiBoard.personal_pack') - : mx.getRoom(pack.id)?.name; + label = isUserId(pack.id) ? t('EmojiBoard.personal_pack') : mx.getRoom(pack.id)?.name; g.push({ id: pack.id, @@ -156,7 +153,7 @@ const useGroups = ( }); return g; - }, [mx, imagePacks, tab]); + }, [mx, imagePacks, tab, t]); const gifGroupItems = useMemo(() => { if (tab !== EmojiBoardTab.Gif) return []; @@ -255,6 +252,7 @@ function EmojiSidebar({ }: Readonly) { const mx = useMatrixClient(); const useAuthentication = useMediaAuthentication(); + const { t } = useTranslation(['room/input']); const [activeGroupId, setActiveGroupId] = useAtom(activeGroupAtom); const usage = ImageUsage.Emoticon; @@ -283,9 +281,7 @@ function EmojiSidebar({ {packs.map((pack) => { let label = pack.meta.name; if (!label) - label = isUserId(pack.id) - ? t('RoomInput.EmojiBoard.personal_pack') - : mx.getRoom(pack.id)?.name; + label = isUserId(pack.id) ? t('EmojiBoard.personal_pack') : mx.getRoom(pack.id)?.name; // limit width and height to 36 to prevent very large icons from breaking the layout, since custom emoji pack icons can be of any size // trying to get close to the render target size of the icons in the sidebar, which is around 24px @@ -298,7 +294,7 @@ function EmojiSidebar({ key={pack.id} active={activeGroupId === pack.id} id={pack.id} - label={label ?? t('RoomInput.EmojiBoard.unknown_pack')} + label={label ?? t('EmojiBoard.unknown_pack')} url={url ?? undefined} onClick={handleScrollToGroup} /> @@ -341,6 +337,7 @@ function StickerSidebar({ saveStickerEmojiBandwidth, onScrollToGroup, }: Readonly) { + const { t } = useTranslation(['room/input']); const mx = useMatrixClient(); const useAuthentication = useMediaAuthentication(); @@ -370,7 +367,7 @@ function StickerSidebar({ key={pack.id} active={activeGroupId === pack.id} id={pack.id} - label={label ?? t('RoomInput.EmojiBoard.unknown_pack')} + label={label ?? t('EmojiBoard.unknown_pack')} url={url ?? undefined} onClick={handleScrollToGroup} /> @@ -483,6 +480,7 @@ export function EmojiBoard({ const mx = useMatrixClient(); const [saveStickerEmojiBandwidth] = useSetting(settingsAtom, 'saveStickerEmojiBandwidth'); const [showGifPicker] = useSetting(settingsAtom, 'enableGifPicker'); + const { t } = useTranslation(['room/input']); const emojiTab = tab === EmojiBoardTab.Emoji; const gifTab = tab === EmojiBoardTab.Gif; @@ -834,8 +832,8 @@ export function EmojiBoard({ id={SEARCH_GROUP_ID} label={ searchedItems.length - ? t('RoomInput.EmojiBoard.search_results') - : t('RoomInput.EmojiBoard.no_results_found') + ? t('EmojiBoard.search_results') + : t('EmojiBoard.no_results_found') } > {searchedItems.map((element, index) => renderItem(element, index))} diff --git a/src/app/features/room/AudioMessageRecorder.tsx b/src/app/features/room/AudioMessageRecorder.tsx index a5518ca76a..f896252625 100644 --- a/src/app/features/room/AudioMessageRecorder.tsx +++ b/src/app/features/room/AudioMessageRecorder.tsx @@ -12,7 +12,7 @@ import { useVoiceRecorder } from '$plugins/voice-recorder-kit'; import type { VoiceRecorderStopPayload } from '$plugins/voice-recorder-kit'; import { Box, Text } from 'folds'; import * as css from './AudioMessageRecorder.css'; -import { t } from 'i18next'; +import { useTranslation } from 'react-i18next'; export type AudioRecordingCompletePayload = { audioBlob: Blob; @@ -55,6 +55,7 @@ export const AudioMessageRecorder = forwardRef< const [isCanceling, setIsCanceling] = useState(false); const [announcedTime, setAnnouncedTime] = useState(0); const [barCount, setBarCount] = useState(MAX_BAR_COUNT); + const { t } = useTranslation(['room/input']); const onRecordingCompleteRef = useRef(onRecordingComplete); onRecordingCompleteRef.current = onRecordingComplete; @@ -197,7 +198,7 @@ export const AudioMessageRecorder = forwardRef< {announcedTime > 0 && announcedTime === seconds && ( - {t('RoomInput.recording_duration')} {formatTime(announcedTime)} + {t('recording_duration')} {formatTime(announcedTime)} )} diff --git a/src/app/features/room/MembersDrawer.tsx b/src/app/features/room/MembersDrawer.tsx index 4bc8e289e5..027eadd7e0 100644 --- a/src/app/features/room/MembersDrawer.tsx +++ b/src/app/features/room/MembersDrawer.tsx @@ -66,7 +66,7 @@ import { formatCompactNumber } from '$utils/formatCompactNumber'; import * as css from './MembersDrawer.css'; import { SidebarResizer } from '$pages/client/sidebar/SidebarResizer'; import { useScreenSizeContext, ScreenSize } from '$hooks/useScreenSize'; -import { t } from 'i18next'; +import { useTranslation } from 'react-i18next'; type MemberDrawerHeaderProps = { room: Room; @@ -74,6 +74,7 @@ type MemberDrawerHeaderProps = { }; function MemberDrawerHeader({ room, hideText }: MemberDrawerHeaderProps) { const setPeopleDrawer = useSetSetting(settingsAtom, 'isPeopleDrawer'); + const { t } = useTranslation(['general']); return (
@@ -92,7 +93,7 @@ function MemberDrawerHeader({ room, hideText }: MemberDrawerHeaderProps) { offset={4} tooltip={ - Close + {t('close')} } > @@ -250,6 +251,7 @@ export function MembersDrawer({ room, members }: MembersDrawerProps) { const creators = useRoomCreators(room); const getPowerTag = useGetMemberPowerTag(room, creators, powerLevels); const getPowerLevel = useGetMemberPowerLevel(powerLevels); + const { t } = useTranslation(['room/drawers/members', 'general']); const fetchingMembers = members.length < room.getJoinedMemberCount(); const openUserRoomProfile = useOpenUserRoomProfile(); @@ -436,7 +438,7 @@ export function MembersDrawer({ room, members }: MembersDrawerProps) { ref={searchInputRef} onChange={handleSearchChange} style={{ paddingRight: config.space.S200 }} - placeholder="Type name..." + placeholder={t('type_name')} variant="Surface" size="400" radii="400" @@ -457,9 +459,13 @@ export function MembersDrawer({ room, members }: MembersDrawerProps) { }} after={chipIcon(X)} > - {`${result.items.length || 'No'} ${ - result.items.length === 1 ? 'Result' : 'Results' - }`} + + {result.items.length} + {t('result', { + ns: 'general', + count: result.items.length, + })} + ) } @@ -474,7 +480,7 @@ export function MembersDrawer({ room, members }: MembersDrawerProps) { radii="Pill" outlined size="300" - aria-label={t('RoomView.scroll_to_top')} + aria-label={t('scroll_to_top')} > {composerIcon(CaretUp)} @@ -482,7 +488,7 @@ export function MembersDrawer({ room, members }: MembersDrawerProps) { {!fetchingMembers && !result && processMembers.length === 0 && ( - {`No "${membershipFilter.name}" Members`} + {t('no_members', { filter: membershipFilter.name })} )} diff --git a/src/app/features/room/RoomCallButton.test.tsx b/src/app/features/room/RoomCallButton.test.tsx index 6fc10e3b19..4c2334d35f 100644 --- a/src/app/features/room/RoomCallButton.test.tsx +++ b/src/app/features/room/RoomCallButton.test.tsx @@ -3,7 +3,7 @@ import { describe, expect, it, vi, beforeEach } from 'vitest'; import type * as JotaiModule from 'jotai'; import type { Room } from '$types/matrix-sdk'; import { RoomCallButton } from './RoomCallButton'; -import { t } from 'i18next'; +import { useTranslation } from 'react-i18next'; const { startCallMock, useCallJoinedMock } = vi.hoisted(() => ({ startCallMock: vi.fn<(...args: unknown[]) => void>(), @@ -24,6 +24,7 @@ vi.mock('jotai', async (importOriginal: () => Promise) => { }); describe('RoomCallButton', () => { + const { t } = useTranslation(['room/room-view/room-buttons']); const room = { roomId: '!room:example.org' } as Room; beforeEach(() => { @@ -41,7 +42,7 @@ describe('RoomCallButton', () => { /> ); - fireEvent.click(screen.getByRole('button', { name: t('RoomView.start_voice_call') })); + fireEvent.click(screen.getByRole('button', { name: t('start_voice_call') })); await waitFor(() => { expect(startCallMock).toHaveBeenCalledWith(room, { @@ -62,7 +63,7 @@ describe('RoomCallButton', () => { /> ); - fireEvent.click(screen.getByRole('button', { name: t('RoomView.start_voice_call') })); + fireEvent.click(screen.getByRole('button', { name: t('start_voice_call') })); await waitFor(() => { expect(startCallMock).toHaveBeenCalledWith(room, { diff --git a/src/app/features/room/RoomCallButton.tsx b/src/app/features/room/RoomCallButton.tsx index 16075e3537..cb2752b81a 100644 --- a/src/app/features/room/RoomCallButton.tsx +++ b/src/app/features/room/RoomCallButton.tsx @@ -5,7 +5,7 @@ import type { Room } from '$types/matrix-sdk'; import { useCallStart, useCallJoined } from '$hooks/useCallEmbed'; import type { CallPreferences } from '$state/callPreferences'; import { callEmbedAtom } from '$state/callEmbed'; -import { t } from 'i18next'; +import { useTranslation } from 'react-i18next'; interface RoomCallButtonProps { room: Room; @@ -22,6 +22,7 @@ export function RoomCallButton({ kind, allowVideoStart = true, }: RoomCallButtonProps) { + const { t } = useTranslation(['room/room-view/room-buttons']); const startCall = useCallStart(direct); const callEmbed = useAtomValue(callEmbedAtom); const joined = useCallJoined(callEmbed); @@ -67,7 +68,7 @@ export function RoomCallButton({ diff --git a/src/app/features/room/RoomTimeline.tsx b/src/app/features/room/RoomTimeline.tsx index 1c4549af33..d3f3479f82 100644 --- a/src/app/features/room/RoomTimeline.tsx +++ b/src/app/features/room/RoomTimeline.tsx @@ -74,7 +74,7 @@ import { } from '$hooks/timeline/useProcessedTimeline'; import { useTimelineEventRenderer } from '$hooks/timeline/useTimelineEventRenderer'; import * as css from './RoomTimeline.css'; -import { t } from 'i18next'; +import { useTranslation } from 'react-i18next'; const TimelineFloat = as<'div', css.TimelineFloatVariants>( ({ position, className, ...props }, ref) => ( @@ -311,6 +311,7 @@ export function RoomTimeline({ ); const [incomingInlineImagesMaxHeight] = useSetting(settingsAtom, 'incomingInlineImagesMaxHeight'); const [hideMemberInReadOnly] = useSetting(settingsAtom, 'hideMembershipInReadOnly'); + const { t } = useTranslation(['room/room-view/timeline-pills', 'general']); const [showInteractiveMap] = useSetting(settingsAtom, 'showInteractiveMap'); const [showEncInteractiveMap] = useSetting(settingsAtom, 'showEncInteractiveMap'); @@ -887,7 +888,7 @@ export function RoomTimeline({ style={{ padding: config.space.S300 }} > - {t('RoomView.failed_to_load_history')} + {t('failed_to_load_history')} timelineSync.handleTimelinePagination(true)} > - {t('General.retry')} + {t('retry', { ns: 'general' })} ); @@ -913,7 +914,7 @@ export function RoomTimeline({ style={{ padding: config.space.S300 }} > - {t('RoomView.failed_to_load_messages')} + {t('failed_to_load_messages')} timelineSync.handleTimelinePagination(false)} > - {t('General.retry')} + {t('retry', { ns: 'general' })} ); @@ -1051,7 +1052,7 @@ export function RoomTimeline({ before={chipIcon(ChatTeardropDots)} onClick={() => timelineSync.loadEventTimeline(unreadInfo.readUptoEventId)} > - {t('RoomView.jump_to_unread')} + {t('jump_to_unread')} markAsRead(mx, room.roomId, hideReads)} > - {t('RoomView.mark_as_read')} + {t('mark_as_read')} )} @@ -1149,7 +1150,7 @@ export function RoomTimeline({ MozUserSelect: 'none', }} > - {t('RoomView.jump_to_latest')} + {t('jump_to_latest')} )} diff --git a/src/app/features/room/RoomTombstone.tsx b/src/app/features/room/RoomTombstone.tsx index 421904e091..3534aabf1a 100644 --- a/src/app/features/room/RoomTombstone.tsx +++ b/src/app/features/room/RoomTombstone.tsx @@ -9,10 +9,11 @@ import { getViaServers } from '$plugins/via-servers'; import { RoomInputPlaceholder } from './RoomInputPlaceholder'; import * as css from './RoomTombstone.css'; import { KnownMembership } from '$types/matrix-sdk'; -import { t } from 'i18next'; +import { useTranslation } from 'react-i18next'; type RoomTombstoneProps = { roomId: string; body?: string; replacementRoomId: string }; export function RoomTombstone({ roomId, body, replacementRoomId }: RoomTombstoneProps) { + const { t } = useTranslation(['room/room-view/replaced-room']); const mx = useMatrixClient(); const { navigateRoom } = useRoomNavigate(); @@ -35,12 +36,10 @@ export function RoomTombstone({ roomId, body, replacementRoomId }: RoomTombstone return ( - - {body || t('RoomView.this_room_has_been_replaced_and_is_no_longer_active')} - + {body || t('this_room_has_been_replaced_and_is_no_longer_active')} {joinState.status === AsyncStatus.Error && ( - {(joinState.error as Error)?.message ?? t('RoomView.failed_to_join_replacement_room')} + {(joinState.error as Error)?.message ?? t('failed_to_join_replacement_room')} )} @@ -48,7 +47,7 @@ export function RoomTombstone({ roomId, body, replacementRoomId }: RoomTombstone {replacementRoom?.getMyMembership() === KnownMembership.Join || joinState.status === AsyncStatus.Success ? ( ) : ( )} diff --git a/src/app/features/room/RoomViewTyping.tsx b/src/app/features/room/RoomViewTyping.tsx index 7716325746..26f2b0a3d9 100644 --- a/src/app/features/room/RoomViewTyping.tsx +++ b/src/app/features/room/RoomViewTyping.tsx @@ -11,7 +11,7 @@ import { useMatrixClient } from '$hooks/useMatrixClient'; import { useRoomTypingMember } from '$hooks/useRoomTypingMembers'; import { nicknamesAtom } from '$state/nicknames'; import * as css from './RoomViewTyping.css'; -import { t } from 'i18next'; +import { useTranslation } from 'react-i18next'; export type RoomViewTypingProps = { room: Room; @@ -22,6 +22,7 @@ export const RoomViewTyping = as<'div', RoomViewTypingProps>( const mx = useMatrixClient(); const typingMembers = useRoomTypingMember(room.roomId); const nicknames = useAtomValue(nicknamesAtom); + const { t } = useTranslation(['room/room-view/typing']); const typingNames = typingMembers .filter((receipt) => receipt.userId !== mx.getUserId()) @@ -63,7 +64,7 @@ export const RoomViewTyping = as<'div', RoomViewTypingProps>( <> {typingNames[0]} - {` ${t('RoomView.is_typing')}`} + {t('is_typing')} )} @@ -71,11 +72,11 @@ export const RoomViewTyping = as<'div', RoomViewTypingProps>( <> {typingNames[0]} - {t('RoomView.typing.typing_sep_word')} + {t('typing_sep_word')} {typingNames[1]} - {t('RoomView.typing.are_typing')} + {t('are_typing')} )} @@ -83,15 +84,15 @@ export const RoomViewTyping = as<'div', RoomViewTypingProps>( <> {typingNames[0]} - {', '} + {t('typing_comma')} {typingNames[1]} - {t('RoomView.typing.typing_sep_word')} + {t('typing_sep_word')} {typingNames[2]} - {t('RoomView.typing.are_typing')} + {t('are_typing')} )} @@ -99,19 +100,22 @@ export const RoomViewTyping = as<'div', RoomViewTypingProps>( <> {typingNames[0]} - {', '} + {t('typing_comma')} {typingNames[1]} - {', '} + {t('typing_comma')} {typingNames[2]} - {t('RoomView.typing.typing_sep_word')} + {t('typing_sep_word')} - {typingNames.length - 3} others + + {typingNames.length - 3} + {t('typing_others')} + - {t('RoomView.typing.are_typing')} + {t('are_typing')} )} diff --git a/src/app/features/room/ThreadBrowser.tsx b/src/app/features/room/ThreadBrowser.tsx index a18fe73b31..9c75d27d1b 100644 --- a/src/app/features/room/ThreadBrowser.tsx +++ b/src/app/features/room/ThreadBrowser.tsx @@ -62,7 +62,7 @@ import { EncryptedContent } from './message'; import * as css from './ThreadDrawer.css'; import { SidebarResizer } from '$pages/client/sidebar/SidebarResizer'; import { mobileOrTablet } from '$utils/user-agent'; -import { t } from 'i18next'; +import { useTranslation } from 'react-i18next'; type ThreadPreviewProps = { room: Room; @@ -83,6 +83,7 @@ function ThreadPreview({ room, thread, onClick, onJump }: ThreadPreviewProps) { const [urlPreview] = useSetting(settingsAtom, 'urlPreview'); const mentionClickHandler = useMentionClickHandler(room.roomId); const spoilerClickHandler = useSpoilerClickHandler(); + const { t } = useTranslation(['room/room-view/drawer.threads', 'general']); const linkifyOpts = useMemo( () => ({ @@ -223,7 +224,7 @@ function ThreadPreview({ room, thread, onClick, onJump }: ThreadPreviewProps) { )} - {t('RoomView.Threads.jump')} + {t('jump', { ns: 'general' })} @@ -316,6 +317,7 @@ export function ThreadBrowser({ room, onOpenThread, onClose, overlay }: ThreadBr const loadingMoreRef = useRef(false); const canLoadMoreRef = useRef(false); canLoadMoreRef.current = canLoadMore; + const { t } = useTranslation(['room/drawers/threads']); // On mount, set up thread event listeners, create the server-side thread // timeline sets, then fetch page 1 via paginate. The two operations are @@ -491,7 +493,7 @@ export function ThreadBrowser({ room, onOpenThread, onClose, overlay }: ThreadBr {composerIcon(Chats)} - {t('RoomView.Threads.threads')} + {t('threads')} @@ -500,7 +502,7 @@ export function ThreadBrowser({ room, onOpenThread, onClose, overlay }: ThreadBr variant="SurfaceVariant" size="300" radii="300" - aria-label={t('RoomView.close_threads')} + aria-label={t('close_threads')} > {composerIcon(X)} @@ -517,7 +519,7 @@ export function ThreadBrowser({ room, onOpenThread, onClose, overlay }: ThreadBr ref={searchRef} value={query} onChange={handleSearchChange} - placeholder={t('RoomView.search_threads')} + placeholder={t('search_threads')} variant="Surface" size="400" radii="400" @@ -532,7 +534,7 @@ export function ThreadBrowser({ room, onOpenThread, onClose, overlay }: ThreadBr setQuery(''); searchRef.current?.focus(); }} - aria-label={t('RoomView.clear_search')} + aria-label={t('clear_search')} > {chipIcon(X)} @@ -572,9 +574,7 @@ export function ThreadBrowser({ room, onOpenThread, onClose, overlay }: ThreadBr > {composerIcon(Chats, { style: { opacity: 0.6 } })} - {lowerQuery - ? t('RoomView.Threads.no_threads_match_your_search') - : t('RoomView.Threads.no_threads_yet')} + {lowerQuery ? t('no_threads_match_your_search') : t('no_threads_yet')} ); diff --git a/src/app/features/room/ThreadDrawer.tsx b/src/app/features/room/ThreadDrawer.tsx index 86ab4255c2..8a8edd790e 100644 --- a/src/app/features/room/ThreadDrawer.tsx +++ b/src/app/features/room/ThreadDrawer.tsx @@ -71,7 +71,7 @@ import { RoomViewFollowing, RoomViewFollowingPlaceholder } from './RoomViewFollo import * as css from './ThreadDrawer.css'; import { SidebarResizer } from '$pages/client/sidebar/SidebarResizer'; import { mobileOrTablet } from '$utils/user-agent'; -import { t } from 'i18next'; +import { useTranslation } from 'react-i18next'; /** * Resolve the list of reply events to show in the thread drawer. @@ -161,6 +161,7 @@ export function ThreadDrawer({ room, threadRootId, onClose, overlay }: ThreadDra const [showInteractiveMap] = useSetting(settingsAtom, 'showInteractiveMap'); const [showEncInteractiveMap] = useSetting(settingsAtom, 'showEncInteractiveMap'); const showMaps = room.hasEncryptionStateEvent() ? showEncInteractiveMap : showInteractiveMap; + const { t } = useTranslation(['room/drawers/threads']); // Memoized parsing options const linkifyOpts = useMemo( @@ -837,7 +838,7 @@ export function ThreadDrawer({ room, threadRootId, onClose, overlay }: ThreadDra {composerIcon(Chats)} - {t('RoomView.Threads.thread')} + {t('thread')} @@ -930,7 +931,7 @@ export function ThreadDrawer({ room, threadRootId, onClose, overlay }: ThreadDra > {composerIcon(Chats, { style: { opacity: 0.6 } })} - {t('RoomView.Threads.no_replies_yet_start_the_thread_below')} + {t('no_replies_yet_start_the_thread_below')} ); @@ -952,7 +953,10 @@ export function ThreadDrawer({ room, threadRootId, onClose, overlay }: ThreadDra }} > - {processedReplies.length} {processedReplies.length === 1 ? 'reply' : 'replies'} + {processedReplies.length} + {t('reply', { + count: processedReplies.length, + })} void; @@ -376,6 +376,7 @@ function MessageInternal( ) { const mx = useMatrixClient(); const useAuthentication = useMediaAuthentication(); + const { t } = useTranslation(['events', 'general']); const [isEmoji, setIsEmoji] = useState(false); @@ -661,10 +662,10 @@ function MessageInternal( const originalRoomId = messageForwardedProps.originalRoomId; return { label: messageForwardedProps.originalEventPrivate - ? t('RoomView.Message.forwarded_private_message') + ? t('forwarded_private_message') : isSameRoomForward(originalRoomId) - ? t('RoomView.Message.forwarded_from_earlier_in_this_room') - : t('RoomView.Message.forwarded_from_another_room'), + ? t('forwarded_from_earlier_in_this_room') + : t('forwarded_from_another_room'), roomId: originalRoomId, eventId: messageForwardedProps.originalEventId, ts: messageForwardedProps.originalTimestamp ?? 0, @@ -676,8 +677,8 @@ function MessageInternal( const originalRoomId = msc2723ForwardedMessageProps.room_id; return { label: isSameRoomForward(originalRoomId) - ? t('RoomView.Message.forwarded_from_earlier_in_this_room') - : t('RoomView.Message.forwarded_from_another_room'), + ? t('forwarded_from_earlier_in_this_room') + : t('forwarded_from_another_room'), roomId: originalRoomId, eventId: msc2723ForwardedMessageProps.event_id, ts: msc2723ForwardedMessageProps.origin_server_ts ?? 0, @@ -686,7 +687,7 @@ function MessageInternal( } return null; - }, [messageForwardedProps, msc2723ForwardedMessageProps, room.roomId]); + }, [messageForwardedProps, msc2723ForwardedMessageProps, room.roomId, t]); const handleResendClick: MouseEventHandler = useCallback( (evt) => { @@ -733,7 +734,7 @@ function MessageInternal( data-mention-event-id={forwardedNotice.eventId} onClick={mentionClickHandler} > - {t('RoomView.Message.jump_to_original')} + {t('jump_to_original')} )} @@ -767,11 +768,11 @@ function MessageInternal( {isFailedSend && ( - {t('RoomView.Message.failed_to_send')} + {t('failed_to_send')} {canResend && ( - {t('General.retry')} + {t('retry', { ns: 'general' })} )} {canDeleteFailedSend && ( @@ -781,7 +782,7 @@ function MessageInternal( radii="Pill" onClick={handleDeleteFailedSendClick} > - {t('General.delete')} + {t('delete', { ns: 'general' })} )} @@ -790,7 +791,7 @@ function MessageInternal( {menuIcon(Info)} - {t('RoomView.Message.only_you_can_see_this')} + {t('only_you_can_see_this')} - {t('General.dismiss')} + {t('dismiss', { ns: 'general' })} )} diff --git a/src/app/features/room/reaction-viewer/ReactionViewer.tsx b/src/app/features/room/reaction-viewer/ReactionViewer.tsx index be34200261..e7f491b7ed 100644 --- a/src/app/features/room/reaction-viewer/ReactionViewer.tsx +++ b/src/app/features/room/reaction-viewer/ReactionViewer.tsx @@ -17,7 +17,7 @@ import { useOpenUserRoomProfile } from '$state/hooks/userRoomProfile'; import { useSpaceOptionally } from '$hooks/useSpace'; import { getMouseEventCords } from '$utils/dom'; import * as css from './ReactionViewer.css'; -import { t } from 'i18next'; +import { useTranslation } from 'react-i18next'; export type ReactionViewerProps = { room: Room; @@ -36,6 +36,7 @@ export const ReactionViewer = as<'div', ReactionViewerProps>( const space = useSpaceOptionally(); const openProfile = useOpenUserRoomProfile(); const nicknames = useAtomValue(nicknamesAtom); + const { t } = useTranslation(['room/drawers/reactions']); const [selectedKey, setSelectedKey] = useState(() => { if (initialKey) return initialKey; @@ -89,7 +90,7 @@ export const ReactionViewer = as<'div', ReactionViewerProps>(
- {t('RoomView.Reactions.reacted_with')} {`:${selectedShortcode}:`} + {t('reacted_with')} {`:${selectedShortcode}:`} diff --git a/src/app/features/settings/cosmetics/Cosmetics.tsx b/src/app/features/settings/cosmetics/Cosmetics.tsx index 8dda0d7372..a9341274c1 100644 --- a/src/app/features/settings/cosmetics/Cosmetics.tsx +++ b/src/app/features/settings/cosmetics/Cosmetics.tsx @@ -208,15 +208,15 @@ function IconSizeSettings() { } function SelectJumboEmojiSize() { - const { t } = useTranslation('general'); + const { t } = useTranslation('settings/appearance'); const emojiSizeItems = [ - { id: 'none', name: t('Settings.Cosmetics.none_same_size_as_text') }, - { id: 'extraSmall', name: t('Settings.Cosmetics.extra_small') }, - { id: 'small', name: t('Settings.Cosmetics.small') }, - { id: 'normal', name: t('Settings.Cosmetics.normal') }, - { id: 'large', name: t('Settings.Cosmetics.large') }, - { id: 'extraLarge', name: t('Settings.Cosmetics.extra_large') }, + { id: 'none', name: t('none_same_size_as_text') }, + { id: 'extraSmall', name: t('extra_small') }, + { id: 'small', name: t('small') }, + { id: 'normal', name: t('normal') }, + { id: 'large', name: t('large') }, + { id: 'extraLarge', name: t('extra_large') }, ]; const [menuCords, setMenuCords] = useState(); @@ -287,12 +287,12 @@ function SelectJumboEmojiSize() { } function SelectRenderCustomProfileCards() { - const { t } = useTranslation('general'); + const { t } = useTranslation('settings/appearance'); const profileCardRenderItems: { id: RenderUserCardsMode; name: string }[] = [ - { id: 'both', name: t('Settings.Cosmetics.light_and_dark') }, - { id: 'light', name: t('Settings.Cosmetics.light_only') }, - { id: 'dark', name: t('Settings.Cosmetics.dark_only') }, - { id: 'none', name: t('Settings.Cosmetics.off') }, + { id: 'both', name: t('light_and_dark') }, + { id: 'light', name: t('light_only') }, + { id: 'dark', name: t('dark_only') }, + { id: 'none', name: t('off') }, ]; const [menuCords, setMenuCords] = useState(); @@ -308,8 +308,7 @@ function SelectRenderCustomProfileCards() { }; const currentLabel = - profileCardRenderItems.find((i) => i.id === renderUserCardsMode)?.name ?? - t('Settings.Cosmetics.light_and_dark'); + profileCardRenderItems.find((i) => i.id === renderUserCardsMode)?.name ?? t('light_and_dark'); return ( <> @@ -365,15 +364,15 @@ function SelectRenderCustomProfileCards() { } function JumboEmoji() { - const { t } = useTranslation('general'); + const { t } = useTranslation('settings/appearance'); return ( - {t('Settings.Cosmetics.jumbo_emoji')} + {t('jumbo_emoji')} } /> @@ -382,7 +381,7 @@ function JumboEmoji() { } function Privacy() { - const { t } = useTranslation('general'); + const { t } = useTranslation('settings/appearance'); const [privacyBlur, setPrivacyBlur] = useSetting(settingsAtom, 'privacyBlur'); const [privacyBlurAvatars, setPrivacyBlurAvatars] = useSetting( settingsAtom, @@ -392,22 +391,22 @@ function Privacy() { return ( - {t('Settings.Cosmetics.privacy_and_security')} + {t('privacy_and_security')} } /> } @@ -416,9 +415,9 @@ function Privacy() { } @@ -429,7 +428,7 @@ function Privacy() { } function IdentityCosmetics() { - const { t } = useTranslation('general'); + const { t } = useTranslation('settings/appearance'); const [legacyUsernameColor, setLegacyUsernameColor] = useSetting( settingsAtom, 'legacyUsernameColor' @@ -446,13 +445,13 @@ function IdentityCosmetics() { return ( - {t('Settings.Cosmetics.identity')} + {t('identity')} } /> @@ -478,9 +477,9 @@ function IdentityCosmetics() { style={{ opacity: showPronouns ? 1 : 0.5 }} > } /> @@ -491,39 +490,37 @@ function IdentityCosmetics() { style={{ opacity: showPronouns ? 1 : 0.5 }} > } /> } /> } /> } @@ -531,11 +528,9 @@ function IdentityCosmetics() { } @@ -543,17 +538,17 @@ function IdentityCosmetics() { } /> } /> diff --git a/src/app/features/settings/cosmetics/LanguageSpecificPronouns.tsx b/src/app/features/settings/cosmetics/LanguageSpecificPronouns.tsx index bfc6457f5a..63034cefb0 100644 --- a/src/app/features/settings/cosmetics/LanguageSpecificPronouns.tsx +++ b/src/app/features/settings/cosmetics/LanguageSpecificPronouns.tsx @@ -4,7 +4,7 @@ import { SequenceCard } from '$components/sequence-card'; import { useEffect, useState } from 'react'; import { getSettings, setSettings } from '$state/settings'; import { SequenceCardStyle } from '../styles.css'; -import { t } from 'i18next'; +import { useTranslation } from 'react-i18next'; export type LanguageSpecificPronounsConfig = { enabled?: boolean | string; @@ -36,6 +36,7 @@ function splitAndTrimLanguages(languages: string): string[] { export function LanguageSpecificPronouns() { const [useLanguageSpecificPronouns, setEnabled] = useState(false); const [languageList, setLanguageList] = useState(''); + const { t } = useTranslation(['settings/appearance']); // common handler for saving changes to the language specific pronouns settings const handleSave = (enabled: boolean, languages: string) => { @@ -70,7 +71,7 @@ export function LanguageSpecificPronouns() { return ( - {t('Settings.Cosmetics.language_specific_pronouns')} + {t('language_specific_pronouns')} {useLanguageSpecificPronouns && ( ({ fileName: stored.fileName, @@ -37,6 +38,7 @@ const toCustomToneMetadata = (stored: StoredCallRingtone): CustomToneMetadata => }); export function CallSoundSettings() { + const { t } = useTranslation(['settings/general']); const [incomingCallSoundEnabled, setIncomingCallSoundEnabled] = useSetting( settingsAtom, 'incomingCallSoundEnabled' @@ -89,11 +91,11 @@ export function CallSoundSettings() { useEffect(() => { if (!loadingCustomState && !hasCustomRingtone && callRingtoneId === 'custom') { setCallRingtoneId('sable-default'); - setCustomError('Custom ringtone is not available on this device. Falling back to default.'); + setCustomError(t('Calls.custom_ringtone_not_available')); } if (!loadingCustomState && !hasCustomRingback && callRingbackTone === 'custom') { setCallRingbackTone('sable-default'); - setCustomError('Custom ringback is not available on this device. Falling back to default.'); + setCustomError(t('Calls.custom_ringback_not_available')); } }, [ callRingtoneId, @@ -103,6 +105,7 @@ export function CallSoundSettings() { loadingCustomState, setCallRingtoneId, setCallRingbackTone, + t, ]); const ringtoneOptions = useMemo( @@ -111,12 +114,12 @@ export function CallSoundSettings() { option.value === 'custom' ? { ...option, - label: customRingtoneMeta ? 'Custom File (Imported)' : 'Custom File', + label: customRingtoneMeta ? t('Calls.custom_file_imported') : t('Calls.custom_file'), disabled: loadingCustomState, } : option ), - [customRingtoneMeta, loadingCustomState] + [customRingtoneMeta, loadingCustomState, t] ); const ringbackOptions = useMemo( () => @@ -124,12 +127,12 @@ export function CallSoundSettings() { option.value === 'custom' ? { ...option, - label: customRingbackMeta ? 'Custom File (Imported)' : 'Custom File', + label: customRingbackMeta ? t('Calls.custom_file_imported') : t('Calls.custom_file'), disabled: loadingCustomState, } : option ), - [customRingbackMeta, loadingCustomState] + [customRingbackMeta, loadingCustomState, t] ); const playPreviewTone = useCallback( @@ -148,12 +151,12 @@ export function CallSoundSettings() { }, 2500); } catch (err) { if (err instanceof Error && err.name === 'AbortError') return; - setCustomError('Unable to preview this ringtone in your browser.'); + setCustomError(t('Calls.unable_to_preview_this_ringtone')); } finally { setPreviewing(false); } }, - [callRingtoneId, callRingbackTone, callRingtoneVolume] + [callRingtoneId, callRingbackTone, callRingtoneVolume, t] ); const importCustomTone = useCallback( @@ -179,20 +182,20 @@ export function CallSoundSettings() { durationMs, }); if (!validation.valid) { - setCustomError(customToneValidationError(validation.reason, label)); + setCustomError(customToneValidationError(validation.reason, label, t)); return; } const stored = await putTone(file, durationMs); onImported(stored); } catch { - setCustomError('Could not import this file. Try a different audio format.'); + setCustomError(t('Calls.could_not_import_file_format')); } }); input.click(); }, - [] + [t] ); const handleImportCustomRingtone = useCallback(() => { @@ -233,7 +236,7 @@ export function CallSoundSettings() { const handleRingtoneSelection = (next: CallRingtoneId) => { if (next === 'custom' && !hasCustomRingtone) { - setCustomError('Import a custom ringtone file first.'); + setCustomError(t('Calls.import_custom_ringtone_file')); return; } setCustomError(null); @@ -242,7 +245,7 @@ export function CallSoundSettings() { const handleRingbackSelection = (next: CallRingtoneId) => { if (next === 'custom' && !hasCustomRingback) { - setCustomError('Import a custom ringback file first.'); + setCustomError(t('Calls.import_custom_ringback_file')); return; } setCustomError(null); @@ -259,9 +262,9 @@ export function CallSoundSettings() { ); } @@ -71,6 +73,7 @@ export function CustomToneSettingsCard({ onPreview: (tone: PreviewTone) => void; onReset: () => void; }) { + const { t } = useTranslation(['settings/general', 'general']); return ( } onClick={onImport} > - Import + {t('import', { ns: 'general' })} {previewActions.map(({ label, tone, icon }) => ( - Max file size: {bytesToSize(CUSTOM_CALL_RINGTONE_MAX_BYTES)}. Max duration:{' '} - {millisecondsToMinutesAndSeconds(CUSTOM_CALL_RINGTONE_MAX_DURATION_MS)}. + {t('Calls.max_file_size', { maxSize: bytesToSize(CUSTOM_CALL_RINGTONE_MAX_BYTES) })} + {t('Calls.max_file_duration', { + maxDuration: millisecondsToMinutesAndSeconds(CUSTOM_CALL_RINGTONE_MAX_DURATION_MS), + })} @@ -136,14 +141,16 @@ export function CustomToneSettingsCard({ export const customToneValidationError = ( reason: 'type' | 'size' | 'duration', - label: 'Ringtone' | 'Ringback' + label: 'Ringtone' | 'Ringback', + t: TFunction ): string => { - if (reason === 'type') return 'Only audio files are supported.'; + if (reason === 'type') return t('Calls.only_audio_files_supported'); if (reason === 'size') { - return `File is too large. Max ${bytesToSize(CUSTOM_CALL_RINGTONE_MAX_BYTES)} allowed.`; + return t('Calls.file_too_large', { maxSize: bytesToSize(CUSTOM_CALL_RINGTONE_MAX_BYTES) }); } - return `${label} must be between 1s and ${millisecondsToMinutesAndSeconds( - CUSTOM_CALL_RINGTONE_MAX_DURATION_MS - )}.`; + return t('Calls.file_too_long', { + label: label, + maxDuration: millisecondsToMinutesAndSeconds(CUSTOM_CALL_RINGTONE_MAX_DURATION_MS), + }); }; diff --git a/src/app/features/settings/general/General.tsx b/src/app/features/settings/general/General.tsx index 510a4bfcf8..16dd78659f 100644 --- a/src/app/features/settings/general/General.tsx +++ b/src/app/features/settings/general/General.tsx @@ -55,7 +55,6 @@ import { isKeyHotkey } from 'is-hotkey'; import { settingsSyncLastSyncedAtom, settingsSyncStatusAtom } from '$hooks/useSettingsSync'; import { exportSettingsAsJson, importSettingsFromJson } from '$utils/settingsSync'; import { SettingsSectionPage } from '../SettingsSectionPage'; -import { t } from 'i18next'; import { CallSoundSettings } from './CallSoundSettings'; import { useTranslation } from 'react-i18next'; import type { SettingMenuOption } from '$components/setting-menu-selector'; @@ -66,6 +65,7 @@ type DateHintProps = { handleReset: () => void; }; function DateHint({ hasChanges, handleReset }: Readonly) { + const { t } = useTranslation(['settings/general', 'general']); const [anchor, setAnchor] = useState(); const categoryPadding = { padding: config.space.S200, paddingTop: 0 }; @@ -88,27 +88,27 @@ function DateHint({ hasChanges, handleReset }: Readonly) { >
- {t('Settings.General.formatting')} + {t('formatting')}
- {t('Settings.General.year')} + {t('year')}
YY {': '} - {t('Settings.General.two_digit_year')} + {t('two_digit_year')} {' '} YYYY {': '} - {t('Settings.General.four_digit_year')} + {t('four_digit_year')} @@ -116,35 +116,35 @@ function DateHint({ hasChanges, handleReset }: Readonly) {
- {t('Settings.General.month')} + {t('month')}
M {': '} - {t('Settings.General.the_month')} + {t('the_month')} MM {': '} - {t('Settings.General.two_digit_month')} + {t('two_digit_month')} {' '} MMM {': '} - {t('Settings.General.short_month_name')} + {t('short_month_name')} MMMM {': '} - {t('Settings.General.full_month_name')} + {t('full_month_name')} @@ -152,55 +152,56 @@ function DateHint({ hasChanges, handleReset }: Readonly) {
- {t('Settings.General.day_of_the_month')} + {t('day_of_the_month')}
D {': '} - {t('Settings.General.day_of_the_month')} + {t('day_of_the_month')} DD - {': '}Two-digit day of the month + {': '} + {t('two_digit_day_of_the_month')}
- {t('Settings.General.day_of_the_week')} + {t('day_of_the_week')}
d {': '} - {t('Settings.General.day_of_the_week_sunday_0')} + {t('day_of_the_week_sunday_0')} dd {': '} - {t('Settings.General.two_letter_day_name')} + {t('two_letter_day_name')} ddd {': '} - {t('Settings.General.short_day_name')} + {t('short_day_name')} dddd {': '} - {t('Settings.General.full_day_name')} + {t('full_day_name')} @@ -243,6 +244,7 @@ type CustomDateFormatProps = { onChange: (format: string) => void; }; function CustomDateFormat({ value, onChange }: Readonly) { + const { t } = useTranslation(['settings/general', 'general']); const [dateFormatCustom, setDateFormatCustom] = useState(value); useEffect(() => { @@ -296,7 +298,7 @@ function CustomDateFormat({ value, onChange }: Readonly) disabled={!hasChanges} type="submit" > - Save + {t('save', { ns: 'general' })}
@@ -383,6 +385,7 @@ function SelectDateFormat() { const [dateFormatString, setDateFormatString] = useSetting(settingsAtom, 'dateFormatString'); const [selectedDateFormat, setSelectedDateFormat] = useState(dateFormatString); const customDateFormat = selectedDateFormat === ''; + const { t } = useTranslation(['settings/general']); const handlePresetChange = (format: string) => { setSelectedDateFormat(format); @@ -394,7 +397,7 @@ function SelectDateFormat() { return ( <> } @@ -408,20 +411,21 @@ function SelectDateFormat() { function getTombstoneSettingToggleTitle(showTombstone: boolean): string { if (showTombstone) { - return 'Disable to hide redacted messages entirely instead of showing a tombstone.'; + return 'disable_to_hide_redacted_messages_entirely'; } - return 'Enable to show tombstone events for redacted messages instead of hiding them entirely.'; + return 'enable_to_show_tombstone_events_for_redacted'; } function DateAndTime() { const [hour24Clock, setHour24Clock] = useSetting(settingsAtom, 'hour24Clock'); + const { t } = useTranslation(['settings/general']); return ( Date & Time } /> @@ -436,6 +440,7 @@ function DateAndTime() { function LanguageChange() { const { i18n } = useTranslation('general'); + const { t } = useTranslation(['settings/general']); const languageOptions: SettingMenuOption[] = [ { value: '', label: 'System' }, @@ -463,7 +468,7 @@ function LanguageChange() { Language ) { settingsAtom, 'sendIndividualAttachmentAsCaption' ); + const { t } = useTranslation(['settings/general']); return ( - Editor - - - } - /> - + {t('Editor.editor')} + {!isMobile && ( + + + } + /> + + )} } /> } @@ -534,33 +534,33 @@ function Editor({ isMobile }: Readonly<{ isMobile: boolean }>) { } /> } /> } /> } @@ -568,9 +568,9 @@ function Editor({ isMobile }: Readonly<{ isMobile: boolean }>) { ) function Gestures({ isMobile }: Readonly<{ isMobile: boolean }>) { const [mobileGestures, setMobileGestures] = useSetting(settingsAtom, 'mobileGestures'); + const { t } = useTranslation(['settings/general']); return ( - Gestures {!isMobile && '(Mobile Only)'} + + {t('Gestures.gestures')} + {!isMobile && t('Gestures.mobile_only')} + ) { } /> @@ -935,6 +939,7 @@ function EmojiSelectorThresholdInput() { } function Calls() { + const { t } = useTranslation(['settings/general']); const [alwaysShowCallButton, setAlwaysShowCallButton] = useSetting( settingsAtom, 'alwaysShowCallButton' @@ -946,10 +951,10 @@ function Calls() { return ( - Calls + {t('Calls.calls')} - Messages + {t('Messages.messages')} } /> } /> } /> @@ -1050,16 +1057,16 @@ function Messages() { {messageLayout === MessageLayout.Bubble && ( } /> )}