From ac70f7ac9b4bf0314a8727a9ab870d5f49d7442a Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 22 Aug 2023 16:32:05 +0100 Subject: [PATCH] Fix instances of double translation and guard translation calls using typescript (#11443) Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> --- src/@types/common.ts | 43 +++++++++++++----- src/IdentityAuthClient.tsx | 4 +- src/LegacyCallHandler.tsx | 12 ++--- src/Lifecycle.ts | 3 +- src/Notifier.ts | 3 +- src/SlashCommands.tsx | 14 ++---- src/TextForEvent.tsx | 31 ++++++------- src/accessibility/KeyboardShortcutUtils.ts | 6 +-- src/accessibility/KeyboardShortcuts.ts | 10 +++-- .../security/CreateSecretStorageDialog.tsx | 13 ++---- .../dialogs/security/ExportE2eKeysDialog.tsx | 13 +----- .../dialogs/security/ImportE2eKeysDialog.tsx | 8 +--- .../security/NewRecoveryMethodDialog.tsx | 5 +-- .../security/RecoveryMethodRemovedDialog.tsx | 12 ++--- src/components/structures/EmbeddedPage.tsx | 4 +- src/components/structures/MatrixChat.tsx | 20 +++------ src/components/structures/RoomStatusBar.tsx | 12 ++--- src/components/structures/SpaceRoomView.tsx | 3 +- src/components/structures/TabbedView.tsx | 4 +- src/components/structures/ThreadPanel.tsx | 3 +- src/components/structures/TimelinePanel.tsx | 3 +- .../WaitingForThirdPartyRoomView.tsx | 3 +- .../structures/auth/ForgotPassword.tsx | 10 ++--- .../structures/auth/Registration.tsx | 3 +- .../structures/auth/SetupEncryptionBody.tsx | 19 +++----- src/components/structures/auth/SoftLogout.tsx | 12 ++--- .../auth/forgot-password/VerifyEmailModal.tsx | 3 +- src/components/views/auth/EmailField.tsx | 14 +++--- .../auth/InteractiveAuthEntryComponents.tsx | 3 +- .../views/auth/PassphraseConfirmField.tsx | 8 ++-- src/components/views/auth/PassphraseField.tsx | 10 ++--- .../views/context_menus/DeviceContextMenu.tsx | 4 +- .../views/context_menus/WidgetContextMenu.tsx | 3 +- .../dialogs/AddExistingToSpaceDialog.tsx | 4 +- .../dialogs/AnalyticsLearnMoreDialog.tsx | 4 +- .../views/dialogs/BugReportDialog.tsx | 10 +---- .../views/dialogs/BulkRedactDialog.tsx | 10 ++--- .../CantStartVoiceMessageBroadcastDialog.tsx | 3 +- .../views/dialogs/ConfirmWipeDeviceDialog.tsx | 3 +- .../views/dialogs/CreateRoomDialog.tsx | 9 ++-- .../views/dialogs/CryptoStoreTooNewDialog.tsx | 8 +--- .../views/dialogs/DevtoolsDialog.tsx | 6 +-- .../views/dialogs/EndPollDialog.tsx | 4 +- .../views/dialogs/FeedbackDialog.tsx | 6 +-- .../views/dialogs/IncomingSasDialog.tsx | 14 ++---- .../dialogs/IntegrationsImpossibleDialog.tsx | 3 +- src/components/views/dialogs/InviteDialog.tsx | 16 +++---- .../dialogs/LazyLoadingDisabledDialog.tsx | 9 +--- .../views/dialogs/LazyLoadingResyncDialog.tsx | 4 +- .../views/dialogs/LeaveSpaceDialog.tsx | 7 +-- src/components/views/dialogs/LogoutDialog.tsx | 8 +--- .../ManageRestrictedJoinRuleDialog.tsx | 3 +- .../dialogs/RegistrationEmailPromptDialog.tsx | 3 +- .../views/dialogs/ReportEventDialog.tsx | 34 ++++---------- .../views/dialogs/RoomUpgradeDialog.tsx | 10 ++--- .../dialogs/RoomUpgradeWarningDialog.tsx | 12 ++--- .../views/dialogs/ServerOfflineDialog.tsx | 3 +- .../views/dialogs/SeshatResetDialog.tsx | 4 +- .../dialogs/SessionRestoreErrorDialog.tsx | 7 +-- .../views/dialogs/SetEmailDialog.tsx | 3 +- .../views/dialogs/SpacePreferencesDialog.tsx | 3 +- .../views/dialogs/StorageEvictedDialog.tsx | 4 +- .../views/dialogs/TextInputDialog.tsx | 4 +- .../views/dialogs/UploadFailureDialog.tsx | 7 ++- .../views/dialogs/devtools/Event.tsx | 4 +- .../dialogs/devtools/VerificationExplorer.tsx | 4 +- .../security/AccessSecretStorageDialog.tsx | 6 +-- .../ConfirmDestroyCrossSigningDialog.tsx | 5 +-- .../security/RestoreKeyBackupDialog.tsx | 19 +++----- .../dialogs/spotlight/SpotlightDialog.tsx | 5 +-- .../elements/DesktopCapturerSourcePicker.tsx | 8 ++-- .../views/elements/ErrorBoundary.tsx | 12 ++--- src/components/views/elements/ReplyChain.tsx | 3 +- .../views/elements/ServerPicker.tsx | 4 +- .../views/elements/SettingsFlag.tsx | 9 ++-- .../views/location/EnableLiveShare.tsx | 5 +-- .../views/messages/DateSeparator.tsx | 11 ++--- .../views/messages/DownloadActionButton.tsx | 4 +- .../views/messages/EncryptionEvent.tsx | 6 +-- .../views/messages/RoomPredecessorTile.tsx | 7 +-- src/components/views/messages/TextualBody.tsx | 4 +- .../views/right_panel/EncryptionInfo.tsx | 6 +-- .../views/right_panel/PinnedMessagesCard.tsx | 3 +- src/components/views/right_panel/UserInfo.tsx | 18 +++----- .../views/right_panel/VerificationPanel.tsx | 4 +- .../views/room_settings/AliasSettings.tsx | 18 +++----- .../room_settings/UrlPreviewSettings.tsx | 7 +-- src/components/views/rooms/E2EIcon.tsx | 8 ++-- src/components/views/rooms/EntityTile.tsx | 4 +- src/components/views/rooms/NewRoomIntro.tsx | 8 ++-- src/components/views/rooms/RoomList.tsx | 4 +- src/components/views/rooms/RoomPreviewBar.tsx | 10 ++--- .../views/rooms/RoomUpgradeWarningBar.tsx | 10 ++--- .../views/rooms/SendMessageComposer.tsx | 5 ++- src/components/views/rooms/Stickerpicker.tsx | 4 +- .../views/rooms/ThirdPartyMemberInfo.tsx | 3 +- .../views/settings/CrossSigningPanel.tsx | 3 +- .../views/settings/EventIndexPanel.tsx | 12 ++--- .../views/settings/JoinRuleSettings.tsx | 7 +-- .../views/settings/SecureBackupPanel.tsx | 11 ++--- src/components/views/settings/SetIdServer.tsx | 28 ++++-------- .../views/settings/SetIntegrationManager.tsx | 5 +-- .../views/settings/account/PhoneNumbers.tsx | 3 +- .../settings/devices/DeviceDetailHeading.tsx | 6 +-- .../devices/DeviceSecurityLearnMore.tsx | 9 ++-- .../settings/devices/FilteredDeviceList.tsx | 5 +-- .../settings/devices/LoginWithQRSection.tsx | 3 +- .../devices/SecurityRecommendations.tsx | 6 +-- .../notifications/NotificationSettings2.tsx | 6 +-- .../tabs/room/AdvancedRoomSettingsTab.tsx | 4 +- .../settings/tabs/room/BridgeSettingsTab.tsx | 4 +- .../tabs/room/NotificationSettingsTab.tsx | 3 +- .../tabs/room/RolesRoomSettingsTab.tsx | 17 ++++--- .../tabs/room/SecurityRoomSettingsTab.tsx | 27 +++-------- .../tabs/room/VoipRoomSettingsTab.tsx | 7 ++- .../tabs/user/GeneralUserSettingsTab.tsx | 3 +- .../tabs/user/HelpUserSettingsTab.tsx | 28 ++++-------- .../tabs/user/LabsUserSettingsTab.tsx | 10 +---- .../tabs/user/MjolnirUserSettingsTab.tsx | 13 ++---- .../tabs/user/SecurityUserSettingsTab.tsx | 3 +- .../settings/tabs/user/SessionManagerTab.tsx | 3 +- .../tabs/user/SidebarUserSettingsTab.tsx | 3 +- .../views/spaces/SpaceCreateMenu.tsx | 3 +- .../user-onboarding/UserOnboardingHeader.tsx | 12 ++--- .../verification/VerificationComplete.tsx | 3 +- src/createRoom.ts | 3 +- src/editor/commands.tsx | 3 +- src/effects/effect.ts | 6 ++- src/languageHandler.tsx | 37 ++++++++++----- src/modules/ProxiedModuleApi.ts | 4 +- .../VectorPushRulesDefinitions.ts | 6 +-- src/phonenumber.ts | 4 +- src/settings/Settings.tsx | 18 ++++---- src/settings/SettingsStore.ts | 2 +- src/slash-commands/command.ts | 6 +-- src/stores/RoomViewStore.tsx | 4 +- src/toasts/AnalyticsToast.tsx | 3 +- src/toasts/MobileGuideToast.ts | 3 +- src/utils/AutoDiscoveryUtils.tsx | 20 ++++----- src/utils/ErrorUtils.tsx | 11 ++--- src/utils/MultiInviter.ts | 3 +- src/utils/PasswordScorer.ts | 7 +-- src/utils/leave-behaviour.ts | 3 +- src/utils/location/LocationShareErrors.ts | 3 +- src/utils/location/positionFailureMessage.ts | 3 +- .../ConfirmListenBroadcastStopCurrent.tsx | 3 +- .../hooks/useVoiceBroadcastRecording.tsx | 3 +- .../checkVoiceBroadcastPreConditions.tsx | 9 ++-- .../utils/showCantStartACallDialog.tsx | 3 +- src/widgets/CapabilityText.tsx | 9 ++-- test/TextForEvent-test.ts | 45 ++++++++++++++++++- test/components/views/beta/BetaCard-test.tsx | 11 ++--- .../discovery/EmailAddresses-test.tsx | 4 +- test/languageHandler-test.tsx | 29 +++++++----- ...erSupportUnstableFeatureController-test.ts | 3 +- test/utils/AutoDiscoveryUtils-test.tsx | 22 +++++++++ .../AutoDiscoveryUtils-test.tsx.snap | 26 +++++++++++ 157 files changed, 554 insertions(+), 780 deletions(-) create mode 100644 test/utils/__snapshots__/AutoDiscoveryUtils-test.tsx.snap diff --git a/src/@types/common.ts b/src/@types/common.ts index 4ea9cff802..dd42c2078f 100644 --- a/src/@types/common.ts +++ b/src/@types/common.ts @@ -23,22 +23,41 @@ export type Writeable = { -readonly [P in keyof T]: T[P] }; export type ComponentClass = keyof JSX.IntrinsicElements | JSXElementConstructor; -// Utility type for string dot notation for accessing nested object properties -// Based on https://stackoverflow.com/a/58436959 -type Join = K extends string | number +/** + * Utility type for string dot notation for accessing nested object properties. + * Based on https://stackoverflow.com/a/58436959 + * @example + * { + * "a": { + * "b": { + * "c": "value" + * }, + * "d": "foobar" + * } + * } + * will yield a type of `"a.b.c" | "a.d"` with Separator="." + * @typeParam Target the target type to generate leaf keys for + * @typeParam Separator the separator to use between key segments when accessing nested objects + * @typeParam LeafType the type which leaves of this object extend, used to determine when to stop recursion + * @typeParam MaxDepth the maximum depth to recurse to + * @returns a union type representing all dot (Separator) string notation keys which can access a Leaf (of LeafType) + */ +export type Leaves = [ + MaxDepth, +] extends [never] + ? never + : Target extends LeafType + ? "" + : { + [K in keyof Target]-?: Join, Separator>; + }[keyof Target]; +type Prev = [never, 0, 1, 2, 3, ...0[]]; +type Join = K extends string | number ? P extends string | number - ? `${K}${"" extends P ? "" : "."}${P}` + ? `${K}${"" extends P ? "" : S}${P}` : never : never; -type Prev = [never, 0, 1, 2, 3, ...0[]]; - -export type Leaves = [D] extends [never] - ? never - : T extends object - ? { [K in keyof T]-?: Join> }[keyof T] - : ""; - export type RecursivePartial = { [P in keyof T]?: T[P] extends (infer U)[] ? RecursivePartial[] diff --git a/src/IdentityAuthClient.tsx b/src/IdentityAuthClient.tsx index 44f0aaf2e6..78dc1ea0af 100644 --- a/src/IdentityAuthClient.tsx +++ b/src/IdentityAuthClient.tsx @@ -141,9 +141,7 @@ export default class IdentityAuthClient {

{_t( - "This action requires accessing the default identity server " + - " to validate an email address or phone number, " + - "but the server does not have any terms of service.", + "This action requires accessing the default identity server to validate an email address or phone number, but the server does not have any terms of service.", {}, { server: () => {abbreviateUrl(identityServerUrl)}, diff --git a/src/LegacyCallHandler.tsx b/src/LegacyCallHandler.tsx index 18ae6b8b8e..1162cde51f 100644 --- a/src/LegacyCallHandler.tsx +++ b/src/LegacyCallHandler.tsx @@ -823,19 +823,14 @@ export default class LegacyCallHandler extends EventEmitter {

{_t( - "Please ask the administrator of your homeserver " + - "(%(homeserverDomain)s) to configure a TURN server in " + - "order for calls to work reliably.", + "Please ask the administrator of your homeserver (%(homeserverDomain)s) to configure a TURN server in order for calls to work reliably.", { homeserverDomain: cli.getDomain() }, { code: (sub: string) => {sub} }, )}

{_t( - "Alternatively, you can try to use the public server at " + - ", but this will not be as reliable, and " + - "it will share your IP address with that server. You can also manage " + - "this in Settings.", + "Alternatively, you can try to use the public server at , but this will not be as reliable, and it will share your IP address with that server. You can also manage this in Settings.", undefined, { server: () => {new URL(FALLBACK_ICE_SERVER).pathname} }, )} @@ -865,8 +860,7 @@ export default class LegacyCallHandler extends EventEmitter { description = (

{_t( - "Call failed because microphone could not be accessed. " + - "Check that a microphone is plugged in and set up correctly.", + "Call failed because microphone could not be accessed. Check that a microphone is plugged in and set up correctly.", )}
); diff --git a/src/Lifecycle.ts b/src/Lifecycle.ts index a5be8f7f5c..48576b3c77 100644 --- a/src/Lifecycle.ts +++ b/src/Lifecycle.ts @@ -305,8 +305,7 @@ export function attemptTokenLogin( logger.warn("Cannot log in with token: can't determine HS URL to use"); onFailedDelegatedAuthLogin( _t( - "We asked the browser to remember which homeserver you use to let you sign in, " + - "but unfortunately your browser has forgotten it. Go to the sign in page and try again.", + "We asked the browser to remember which homeserver you use to let you sign in, but unfortunately your browser has forgotten it. Go to the sign in page and try again.", ), ); return Promise.resolve(false); diff --git a/src/Notifier.ts b/src/Notifier.ts index 839be8c83a..cf9bafdcab 100644 --- a/src/Notifier.ts +++ b/src/Notifier.ts @@ -299,8 +299,7 @@ class NotifierClass { const description = result === "denied" ? _t( - "%(brand)s does not have permission to send you notifications - " + - "please check your browser settings", + "%(brand)s does not have permission to send you notifications - please check your browser settings", { brand }, ) : _t("%(brand)s was not given permission to send notifications - please try again", { diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx index 523faec5c8..2ec6d3dd27 100644 --- a/src/SlashCommands.tsx +++ b/src/SlashCommands.tsx @@ -192,8 +192,7 @@ export const Commands = [ const unixTimestamp = Date.parse(args); if (!unixTimestamp) { throw new UserFriendlyError( - "We were unable to understand the given date (%(inputDate)s). " + - "Try using the format YYYY-MM-DD.", + "We were unable to understand the given date (%(inputDate)s). Try using the format YYYY-MM-DD.", { inputDate: args, cause: undefined }, ); } @@ -401,9 +400,7 @@ export const Commands = [ description: (

{_t( - "Use an identity server to invite by email. " + - "Click continue to use the default identity server " + - "(%(defaultIdentityServerName)s) or manage in Settings.", + "Use an identity server to invite by email. Click continue to use the default identity server (%(defaultIdentityServerName)s) or manage in Settings.", { defaultIdentityServerName: abbreviateUrl(defaultIdentityServerUrl), }, @@ -717,9 +714,7 @@ export const Commands = [ if (device.getFingerprint() !== fingerprint) { const fprint = device.getFingerprint(); throw new UserFriendlyError( - "WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and session" + - ' %(deviceId)s is "%(fprint)s" which does not match the provided key ' + - '"%(fingerprint)s". This could mean your communications are being intercepted!', + 'WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and session %(deviceId)s is "%(fprint)s" which does not match the provided key "%(fingerprint)s". This could mean your communications are being intercepted!', { fprint, userId, @@ -739,8 +734,7 @@ export const Commands = [

{_t( - "The signing key you provided matches the signing key you received " + - "from %(userId)s's session %(deviceId)s. Session marked as verified.", + "The signing key you provided matches the signing key you received from %(userId)s's session %(deviceId)s. Session marked as verified.", { userId, deviceId }, )}

diff --git a/src/TextForEvent.tsx b/src/TextForEvent.tsx index 93896cd0aa..c4aa4416a9 100644 --- a/src/TextForEvent.tsx +++ b/src/TextForEvent.tsx @@ -482,17 +482,14 @@ function textForHistoryVisibilityEvent(event: MatrixEvent): (() => string) | nul case HistoryVisibility.Invited: return () => _t( - "%(senderName)s made future room history visible to all room members, " + - "from the point they are invited.", + "%(senderName)s made future room history visible to all room members, from the point they are invited.", { senderName }, ); case HistoryVisibility.Joined: return () => - _t( - "%(senderName)s made future room history visible to all room members, " + - "from the point they joined.", - { senderName }, - ); + _t("%(senderName)s made future room history visible to all room members, from the point they joined.", { + senderName, + }); case HistoryVisibility.Shared: return () => _t("%(senderName)s made future room history visible to all room members.", { senderName }); case HistoryVisibility.WorldReadable: @@ -810,33 +807,31 @@ function textForMjolnirEvent(event: MatrixEvent): (() => string) | null { if (USER_RULE_TYPES.includes(event.getType())) { return () => _t( - "%(senderName)s changed a rule that was banning users matching %(oldGlob)s to matching " + - "%(newGlob)s for %(reason)s", + "%(senderName)s changed a rule that was banning users matching %(oldGlob)s to matching %(newGlob)s for %(reason)s", { senderName, oldGlob: prevEntity, newGlob: entity, reason }, ); } else if (ROOM_RULE_TYPES.includes(event.getType())) { return () => _t( - "%(senderName)s changed a rule that was banning rooms matching %(oldGlob)s to matching " + - "%(newGlob)s for %(reason)s", + "%(senderName)s changed a rule that was banning rooms matching %(oldGlob)s to matching %(newGlob)s for %(reason)s", { senderName, oldGlob: prevEntity, newGlob: entity, reason }, ); } else if (SERVER_RULE_TYPES.includes(event.getType())) { return () => _t( - "%(senderName)s changed a rule that was banning servers matching %(oldGlob)s to matching " + - "%(newGlob)s for %(reason)s", + "%(senderName)s changed a rule that was banning servers matching %(oldGlob)s to matching %(newGlob)s for %(reason)s", { senderName, oldGlob: prevEntity, newGlob: entity, reason }, ); } // Unknown type. We'll say something but we shouldn't end up here. return () => - _t( - "%(senderName)s updated a ban rule that was matching %(oldGlob)s to matching %(newGlob)s " + - "for %(reason)s", - { senderName, oldGlob: prevEntity, newGlob: entity, reason }, - ); + _t("%(senderName)s updated a ban rule that was matching %(oldGlob)s to matching %(newGlob)s for %(reason)s", { + senderName, + oldGlob: prevEntity, + newGlob: entity, + reason, + }); } export function textForLocationEvent(event: MatrixEvent): () => string { diff --git a/src/accessibility/KeyboardShortcutUtils.ts b/src/accessibility/KeyboardShortcutUtils.ts index acbf14d756..8b6ac184f9 100644 --- a/src/accessibility/KeyboardShortcutUtils.ts +++ b/src/accessibility/KeyboardShortcutUtils.ts @@ -25,9 +25,9 @@ import { IKeyboardShortcuts, KeyBindingAction, KEYBOARD_SHORTCUTS, + KeyboardShortcutSetting, MAC_ONLY_SHORTCUTS, } from "./KeyboardShortcuts"; -import { IBaseSetting } from "../settings/Settings"; /** * This function gets the keyboard shortcuts that should be presented in the UI @@ -115,7 +115,7 @@ export const getKeyboardShortcuts = (): IKeyboardShortcuts => { export const getKeyboardShortcutsForUI = (): IKeyboardShortcuts => { const entries = [...Object.entries(getUIOnlyShortcuts()), ...Object.entries(getKeyboardShortcuts())] as [ KeyBindingAction, - IBaseSetting, + KeyboardShortcutSetting, ][]; return entries.reduce((acc, [key, value]) => { @@ -130,5 +130,5 @@ export const getKeyboardShortcutValue = (name: KeyBindingAction): KeyCombo | und export const getKeyboardShortcutDisplayName = (name: KeyBindingAction): string | undefined => { const keyboardShortcutDisplayName = getKeyboardShortcutsForUI()[name]?.displayName; - return keyboardShortcutDisplayName && _t(keyboardShortcutDisplayName as string); + return keyboardShortcutDisplayName && _t(keyboardShortcutDisplayName); }; diff --git a/src/accessibility/KeyboardShortcuts.ts b/src/accessibility/KeyboardShortcuts.ts index bcd720ee21..67a835f546 100644 --- a/src/accessibility/KeyboardShortcuts.ts +++ b/src/accessibility/KeyboardShortcuts.ts @@ -16,7 +16,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { _td } from "../languageHandler"; +import { _td, TranslationKey } from "../languageHandler"; import { IS_MAC, Key } from "../Keyboard"; import { IBaseSetting } from "../settings/Settings"; import { KeyCombo } from "../KeyBindingsManager"; @@ -154,13 +154,15 @@ export enum KeyBindingAction { ToggleHiddenEventVisibility = "KeyBinding.toggleHiddenEventVisibility", } -type KeyboardShortcutSetting = Omit, "supportedLevels">; +export type KeyboardShortcutSetting = Omit, "supportedLevels" | "displayName"> & { + displayName?: TranslationKey; +}; // TODO: We should figure out what to do with the keyboard shortcuts that are not handled by KeybindingManager export type IKeyboardShortcuts = Partial>; export interface ICategory { - categoryLabel?: string; + categoryLabel?: TranslationKey; // TODO: We should figure out what to do with the keyboard shortcuts that are not handled by KeybindingManager settingNames: KeyBindingAction[]; } @@ -179,7 +181,7 @@ export enum CategoryName { // Meta-key representing the digits [0-9] often found at the top of standard keyboard layouts export const DIGITS = "digits"; -export const ALTERNATE_KEY_NAME: Record = { +export const ALTERNATE_KEY_NAME: Record = { [Key.PAGE_UP]: _td("Page Up"), [Key.PAGE_DOWN]: _td("Page Down"), [Key.ESCAPE]: _td("Esc"), diff --git a/src/async-components/views/dialogs/security/CreateSecretStorageDialog.tsx b/src/async-components/views/dialogs/security/CreateSecretStorageDialog.tsx index 2a0cf58916..9c28f4f05b 100644 --- a/src/async-components/views/dialogs/security/CreateSecretStorageDialog.tsx +++ b/src/async-components/views/dialogs/security/CreateSecretStorageDialog.tsx @@ -559,8 +559,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent

{_t( - "Safeguard against losing access to encrypted messages & data by " + - "backing up encryption keys on your server.", + "Safeguard against losing access to encrypted messages & data by backing up encryption keys on your server.", )}

@@ -611,9 +610,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent

{_t( - "Upgrade this session to allow it to verify other sessions, " + - "granting them access to encrypted messages and marking them " + - "as trusted for other users.", + "Upgrade this session to allow it to verify other sessions, granting them access to encrypted messages and marking them as trusted for other users.", )}

{authPrompt}
@@ -636,8 +633,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent

{_t( - "Enter a Security Phrase only you know, as it's used to safeguard your data. " + - "To be secure, you shouldn't re-use your account password.", + "Enter a Security Phrase only you know, as it's used to safeguard your data. To be secure, you shouldn't re-use your account password.", )}

@@ -752,8 +748,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent

{_t( - "Store your Security Key somewhere safe, like a password manager or a safe, " + - "as it's used to safeguard your encrypted data.", + "Store your Security Key somewhere safe, like a password manager or a safe, as it's used to safeguard your encrypted data.", )}

diff --git a/src/async-components/views/dialogs/security/ExportE2eKeysDialog.tsx b/src/async-components/views/dialogs/security/ExportE2eKeysDialog.tsx index 4574dd7b4f..eaa4d19de2 100644 --- a/src/async-components/views/dialogs/security/ExportE2eKeysDialog.tsx +++ b/src/async-components/views/dialogs/security/ExportE2eKeysDialog.tsx @@ -164,21 +164,12 @@ export default class ExportE2eKeysDialog extends React.Component

{_t( - "This process allows you to export the keys for messages " + - "you have received in encrypted rooms to a local file. You " + - "will then be able to import the file into another Matrix " + - "client in the future, so that client will also be able to " + - "decrypt these messages.", + "This process allows you to export the keys for messages you have received in encrypted rooms to a local file. You will then be able to import the file into another Matrix client in the future, so that client will also be able to decrypt these messages.", )}

{_t( - "The exported file will allow anyone who can read it to decrypt " + - "any encrypted messages that you can see, so you should be " + - "careful to keep it secure. To help with this, you should enter " + - "a unique passphrase below, which will only be used to encrypt the " + - "exported data. " + - "It will only be possible to import the data by using the same passphrase.", + "The exported file will allow anyone who can read it to decrypt any encrypted messages that you can see, so you should be careful to keep it secure. To help with this, you should enter a unique passphrase below, which will only be used to encrypt the exported data. It will only be possible to import the data by using the same passphrase.", )}

{this.state.errStr}
diff --git a/src/async-components/views/dialogs/security/ImportE2eKeysDialog.tsx b/src/async-components/views/dialogs/security/ImportE2eKeysDialog.tsx index e9bf268cba..dbc3c6cff7 100644 --- a/src/async-components/views/dialogs/security/ImportE2eKeysDialog.tsx +++ b/src/async-components/views/dialogs/security/ImportE2eKeysDialog.tsx @@ -146,16 +146,12 @@ export default class ImportE2eKeysDialog extends React.Component

{_t( - "This process allows you to import encryption keys " + - "that you had previously exported from another Matrix " + - "client. You will then be able to decrypt any " + - "messages that the other client could decrypt.", + "This process allows you to import encryption keys that you had previously exported from another Matrix client. You will then be able to decrypt any messages that the other client could decrypt.", )}

{_t( - "The export file will be protected with a passphrase. " + - "You should enter the passphrase here, to decrypt the file.", + "The export file will be protected with a passphrase. You should enter the passphrase here, to decrypt the file.", )}

{this.state.errStr}
diff --git a/src/async-components/views/dialogs/security/NewRecoveryMethodDialog.tsx b/src/async-components/views/dialogs/security/NewRecoveryMethodDialog.tsx index a7aa464b8d..b01fbb4b19 100644 --- a/src/async-components/views/dialogs/security/NewRecoveryMethodDialog.tsx +++ b/src/async-components/views/dialogs/security/NewRecoveryMethodDialog.tsx @@ -62,10 +62,7 @@ export default class NewRecoveryMethodDialog extends React.PureComponent const hackWarning = (

{_t( - "If you didn't set the new recovery method, an " + - "attacker may be trying to access your account. " + - "Change your account password and set a new recovery " + - "method immediately in Settings.", + "If you didn't set the new recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.", )}

); diff --git a/src/async-components/views/dialogs/security/RecoveryMethodRemovedDialog.tsx b/src/async-components/views/dialogs/security/RecoveryMethodRemovedDialog.tsx index b5983b66c0..f91d650469 100644 --- a/src/async-components/views/dialogs/security/RecoveryMethodRemovedDialog.tsx +++ b/src/async-components/views/dialogs/security/RecoveryMethodRemovedDialog.tsx @@ -53,23 +53,17 @@ export default class RecoveryMethodRemovedDialog extends React.PureComponent

{_t( - "This session has detected that your Security Phrase and key " + - "for Secure Messages have been removed.", + "This session has detected that your Security Phrase and key for Secure Messages have been removed.", )}

{_t( - "If you did this accidentally, you can setup Secure Messages on " + - "this session which will re-encrypt this session's message " + - "history with a new recovery method.", + "If you did this accidentally, you can setup Secure Messages on this session which will re-encrypt this session's message history with a new recovery method.", )}

{_t( - "If you didn't remove the recovery method, an " + - "attacker may be trying to access your account. " + - "Change your account password and set a new recovery " + - "method immediately in Settings.", + "If you didn't remove the recovery method, an attacker may be trying to access your account. Change your account password and set a new recovery method immediately in Settings.", )}

{ }; } - private translate(s: string): string { + private translate(s: TranslationKey): string { return sanitizeHtml(_t(s)); } diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index 9002beaa11..ace949b94d 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -482,12 +482,10 @@ export default class MatrixChat extends React.PureComponent { const waitText = _t("Wait!"); const scamText = _t( - "If someone told you to copy/paste something here, " + "there is a high likelihood you're being scammed!", + "If someone told you to copy/paste something here, there is a high likelihood you're being scammed!", ); const devText = _t( - "If you know what you're doing, Element is open-source, " + - "be sure to check out our GitHub (https://github.com/vector-im/element-web/) " + - "and contribute!", + "If you know what you're doing, Element is open-source, be sure to check out our GitHub (https://github.com/vector-im/element-web/) and contribute!", ); global.mx_rage_logger.bypassRageshake( @@ -1166,8 +1164,7 @@ export default class MatrixChat extends React.PureComponent { {" " /* Whitespace, otherwise the sentences get smashed together */} {_t( - "You are the only person here. " + - "If you leave, no one will be able to join in the future, including you.", + "You are the only person here. If you leave, no one will be able to join in the future, including you.", )} , ); @@ -1593,8 +1590,7 @@ export default class MatrixChat extends React.PureComponent {

{" "} {_t( - "To continue using the %(homeserverDomain)s homeserver " + - "you must review and agree to our terms and conditions.", + "To continue using the %(homeserverDomain)s homeserver you must review and agree to our terms and conditions.", { homeserverDomain: cli.getDomain() }, )}

@@ -1643,13 +1639,7 @@ export default class MatrixChat extends React.PureComponent { Modal.createDialog(ErrorDialog, { title: _t("Old cryptography data detected"), description: _t( - "Data from an older version of %(brand)s has been detected. " + - "This will have caused end-to-end cryptography to malfunction " + - "in the older version. End-to-end encrypted messages exchanged " + - "recently whilst using the older version may not be decryptable " + - "in this version. This may also cause messages exchanged with this " + - "version to fail. If you experience problems, log out and back in " + - "again. To retain message history, export and re-import your keys.", + "Data from an older version of %(brand)s has been detected. This will have caused end-to-end cryptography to malfunction in the older version. End-to-end encrypted messages exchanged recently whilst using the older version may not be decryptable in this version. This may also cause messages exchanged with this version to fail. If you experience problems, log out and back in again. To retain message history, export and re-import your keys.", { brand: SdkConfig.get().brand }, ), }); diff --git a/src/components/structures/RoomStatusBar.tsx b/src/components/structures/RoomStatusBar.tsx index 98039b1abc..58c8225ab9 100644 --- a/src/components/structures/RoomStatusBar.tsx +++ b/src/components/structures/RoomStatusBar.tsx @@ -207,8 +207,7 @@ export default class RoomStatusBar extends React.PureComponent { } if (consentError) { title = _t( - "You can't send any messages until you review and agree to " + - "our terms and conditions.", + "You can't send any messages until you review and agree to our terms and conditions.", {}, { consentLink: (sub) => ( @@ -224,16 +223,13 @@ export default class RoomStatusBar extends React.PureComponent { resourceLimitError.data.admin_contact, { "monthly_active_user": _td( - "Your message wasn't sent because this homeserver has hit its Monthly Active User Limit. " + - "Please contact your service administrator to continue using the service.", + "Your message wasn't sent because this homeserver has hit its Monthly Active User Limit. Please contact your service administrator to continue using the service.", ), "hs_disabled": _td( - "Your message wasn't sent because this homeserver has been blocked by its administrator. " + - "Please contact your service administrator to continue using the service.", + "Your message wasn't sent because this homeserver has been blocked by its administrator. Please contact your service administrator to continue using the service.", ), "": _td( - "Your message wasn't sent because this homeserver has exceeded a resource limit. " + - "Please contact your service administrator to continue using the service.", + "Your message wasn't sent because this homeserver has exceeded a resource limit. Please contact your service administrator to continue using the service.", ), }, ); diff --git a/src/components/structures/SpaceRoomView.tsx b/src/components/structures/SpaceRoomView.tsx index 06357fda0e..cf47e53047 100644 --- a/src/components/structures/SpaceRoomView.tsx +++ b/src/components/structures/SpaceRoomView.tsx @@ -401,8 +401,7 @@ const SpaceAddExistingRooms: React.FC<{

{_t("What do you want to organise?")}

{_t( - "Pick rooms or conversations to add. This is just a space for you, " + - "no one will be informed. You can add more later.", + "Pick rooms or conversations to add. This is just a space for you, no one will be informed. You can add more later.", )}
diff --git a/src/components/structures/TabbedView.tsx b/src/components/structures/TabbedView.tsx index b6369466fe..61e34d2d0d 100644 --- a/src/components/structures/TabbedView.tsx +++ b/src/components/structures/TabbedView.tsx @@ -20,7 +20,7 @@ import * as React from "react"; import classNames from "classnames"; import { logger } from "matrix-js-sdk/src/logger"; -import { _t } from "../../languageHandler"; +import { _t, TranslationKey } from "../../languageHandler"; import AutoHideScrollbar from "./AutoHideScrollbar"; import { PosthogScreenTracker, ScreenName } from "../../PosthogTrackers"; import { NonEmptyArray } from "../../@types/common"; @@ -40,7 +40,7 @@ export class Tab { */ public constructor( public readonly id: T, - public readonly label: string, + public readonly label: TranslationKey, public readonly icon: string | null, public readonly body: React.ReactNode, public readonly screenName?: ScreenName, diff --git a/src/components/structures/ThreadPanel.tsx b/src/components/structures/ThreadPanel.tsx index aacea7cd16..c4267d7063 100644 --- a/src/components/structures/ThreadPanel.tsx +++ b/src/components/structures/ThreadPanel.tsx @@ -147,8 +147,7 @@ const EmptyThread: React.FC = ({ hasThreads, filterOption, sh <>

{_t( - "Reply to an ongoing thread or use “%(replyInThread)s” " + - "when hovering over a message to start a new one.", + "Reply to an ongoing thread or use “%(replyInThread)s” when hovering over a message to start a new one.", { replyInThread: _t("Reply in thread"), }, diff --git a/src/components/structures/TimelinePanel.tsx b/src/components/structures/TimelinePanel.tsx index 8cf3edd223..0b77d5daa9 100644 --- a/src/components/structures/TimelinePanel.tsx +++ b/src/components/structures/TimelinePanel.tsx @@ -1651,8 +1651,7 @@ class TimelinePanel extends React.Component { let description: string; if (error.errcode == "M_FORBIDDEN") { description = _t( - "Tried to load a specific point in this room's timeline, but you " + - "do not have permission to view the message in question.", + "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.", ); } else { description = _t("Tried to load a specific point in this room's timeline, but was unable to find it."); diff --git a/src/components/structures/WaitingForThirdPartyRoomView.tsx b/src/components/structures/WaitingForThirdPartyRoomView.tsx index 8b1fe716f9..418199d5d9 100644 --- a/src/components/structures/WaitingForThirdPartyRoomView.tsx +++ b/src/components/structures/WaitingForThirdPartyRoomView.tsx @@ -75,8 +75,7 @@ export const WaitingForThirdPartyRoomView: React.FC = ({ roomView, resize className="mx_cryptoEvent mx_cryptoEvent_icon" title={_t("Waiting for users to join %(brand)s", { brand })} subtitle={_t( - "Once invited users have joined %(brand)s, " + - "you will be able to chat and the room will be end-to-end encrypted", + "Once invited users have joined %(brand)s, you will be able to chat and the room will be end-to-end encrypted", { brand }, )} /> diff --git a/src/components/structures/auth/ForgotPassword.tsx b/src/components/structures/auth/ForgotPassword.tsx index 14d58f372f..fd9277426e 100644 --- a/src/components/structures/auth/ForgotPassword.tsx +++ b/src/components/structures/auth/ForgotPassword.tsx @@ -354,14 +354,12 @@ export default class ForgotPassword extends React.Component {

{_t( - "Signing out your devices will delete the message encryption keys stored on them, " + - "making encrypted chat history unreadable.", + "Signing out your devices will delete the message encryption keys stored on them, making encrypted chat history unreadable.", )}

{_t( - "If you want to retain access to your chat history in encrypted rooms, set up Key Backup " + - "or export your message keys from one of your other devices before proceeding.", + "If you want to retain access to your chat history in encrypted rooms, set up Key Backup or export your message keys from one of your other devices before proceeding.", )}

@@ -443,9 +441,7 @@ export default class ForgotPassword extends React.Component { {this.state.logoutDevices ? (

{_t( - "You have been logged out of all devices and will no longer receive " + - "push notifications. To re-enable notifications, sign in again on each " + - "device.", + "You have been logged out of all devices and will no longer receive push notifications. To re-enable notifications, sign in again on each device.", )}

) : null} diff --git a/src/components/structures/auth/Registration.tsx b/src/components/structures/auth/Registration.tsx index 37edecdd35..53f0576ea4 100644 --- a/src/components/structures/auth/Registration.tsx +++ b/src/components/structures/auth/Registration.tsx @@ -622,8 +622,7 @@ export default class Registration extends React.Component {

{_t( - "Your new account (%(newAccountId)s) is registered, but you're already " + - "logged into a different account (%(loggedInUserId)s).", + "Your new account (%(newAccountId)s) is registered, but you're already logged into a different account (%(loggedInUserId)s).", { newAccountId: this.state.registeredUsername, loggedInUserId: this.state.differentLoggedInUserId, diff --git a/src/components/structures/auth/SetupEncryptionBody.tsx b/src/components/structures/auth/SetupEncryptionBody.tsx index 1de7cc7d83..5562b8a14e 100644 --- a/src/components/structures/auth/SetupEncryptionBody.tsx +++ b/src/components/structures/auth/SetupEncryptionBody.tsx @@ -161,10 +161,7 @@ export default class SetupEncryptionBody extends React.Component

{_t( - "It looks like you don't have a Security Key or any other devices you can " + - "verify against. This device will not be able to access old encrypted messages. " + - "In order to verify your identity on this device, you'll need to reset " + - "your verification keys.", + "It looks like you don't have a Security Key or any other devices you can verify against. This device will not be able to access old encrypted messages. In order to verify your identity on this device, you'll need to reset your verification keys.", )}

@@ -234,8 +231,7 @@ export default class SetupEncryptionBody extends React.Component message = (

{_t( - "Your new device is now verified. It has access to your " + - "encrypted messages, and other users will see it as trusted.", + "Your new device is now verified. It has access to your encrypted messages, and other users will see it as trusted.", )}

); @@ -258,8 +254,7 @@ export default class SetupEncryptionBody extends React.Component

{_t( - "Without verifying, you won't have access to all your messages " + - "and may appear as untrusted to others.", + "Without verifying, you won't have access to all your messages and may appear as untrusted to others.", )}

@@ -277,16 +272,12 @@ export default class SetupEncryptionBody extends React.Component

{_t( - "Resetting your verification keys cannot be undone. After resetting, " + - "you won't have access to old encrypted messages, and any friends who " + - "have previously verified you will see security warnings until you " + - "re-verify with them.", + "Resetting your verification keys cannot be undone. After resetting, you won't have access to old encrypted messages, and any friends who have previously verified you will see security warnings until you re-verify with them.", )}

{_t( - "Please only proceed if you're sure you've lost all of your other " + - "devices and your Security Key.", + "Please only proceed if you're sure you've lost all of your other devices and your Security Key.", )}

diff --git a/src/components/structures/auth/SoftLogout.tsx b/src/components/structures/auth/SoftLogout.tsx index cf4ff3642b..20838f2e62 100644 --- a/src/components/structures/auth/SoftLogout.tsx +++ b/src/components/structures/auth/SoftLogout.tsx @@ -281,8 +281,7 @@ export default class SoftLogout extends React.Component { let introText: string | null = null; // null is translated to something area specific in this function if (this.state.keyBackupNeeded) { introText = _t( - "Regain access to your account and recover encryption keys stored in this session. " + - "Without them, you won't be able to read all of your secure messages in any session.", + "Regain access to your account and recover encryption keys stored in this session. Without them, you won't be able to read all of your secure messages in any session.", ); } @@ -329,10 +328,7 @@ export default class SoftLogout extends React.Component { // Default: assume unsupported/error return (

- {_t( - "You cannot sign in to your account. Please contact your " + - "homeserver admin for more information.", - )} + {_t("You cannot sign in to your account. Please contact your homeserver admin for more information.")}

); } @@ -350,9 +346,7 @@ export default class SoftLogout extends React.Component {

{_t("Clear personal data")}

{_t( - "Warning: your personal data (including encryption keys) is still stored " + - "in this session. Clear it if you're finished using this session, or want to sign " + - "in to another account.", + "Warning: your personal data (including encryption keys) is still stored in this session. Clear it if you're finished using this session, or want to sign in to another account.", )}

diff --git a/src/components/structures/auth/forgot-password/VerifyEmailModal.tsx b/src/components/structures/auth/forgot-password/VerifyEmailModal.tsx index 692ff7c42f..d12ac03d8e 100644 --- a/src/components/structures/auth/forgot-password/VerifyEmailModal.tsx +++ b/src/components/structures/auth/forgot-password/VerifyEmailModal.tsx @@ -54,8 +54,7 @@ export const VerifyEmailModal: React.FC = ({

{_t("Verify your email to continue")}

{_t( - "We need to know it’s you before resetting your password. " + - "Click the link in the email we just sent to %(email)s", + "We need to know it’s you before resetting your password. Click the link in the email we just sent to %(email)s", { email, }, diff --git a/src/components/views/auth/EmailField.tsx b/src/components/views/auth/EmailField.tsx index 849f38fea7..16fa73771c 100644 --- a/src/components/views/auth/EmailField.tsx +++ b/src/components/views/auth/EmailField.tsx @@ -17,7 +17,7 @@ limitations under the License. import React, { PureComponent, RefCallback, RefObject } from "react"; import Field, { IInputProps } from "../elements/Field"; -import { _t, _td } from "../../../languageHandler"; +import { _t, _td, TranslationKey } from "../../../languageHandler"; import withValidation, { IFieldState, IValidationResult } from "../elements/Validation"; import * as Email from "../../../email"; @@ -27,9 +27,9 @@ interface IProps extends Omit { value: string; autoFocus?: boolean; - label?: string; - labelRequired?: string; - labelInvalid?: string; + label: TranslationKey; + labelRequired: TranslationKey; + labelInvalid: TranslationKey; // When present, completely overrides the default validation rules. validationRules?: (fieldState: IFieldState) => Promise; @@ -50,12 +50,12 @@ class EmailField extends PureComponent { { key: "required", test: ({ value, allowEmpty }) => allowEmpty || !!value, - invalid: () => _t(this.props.labelRequired!), + invalid: () => _t(this.props.labelRequired), }, { key: "email", test: ({ value }) => !value || Email.looksValid(value), - invalid: () => _t(this.props.labelInvalid!), + invalid: () => _t(this.props.labelInvalid), }, ], }); @@ -80,7 +80,7 @@ class EmailField extends PureComponent { id={this.props.id} ref={this.props.fieldRef} type="text" - label={_t(this.props.label!)} + label={_t(this.props.label)} value={this.props.value} autoFocus={this.props.autoFocus} onChange={this.props.onChange} diff --git a/src/components/views/auth/InteractiveAuthEntryComponents.tsx b/src/components/views/auth/InteractiveAuthEntryComponents.tsx index add3638fcb..de6f07be1d 100644 --- a/src/components/views/auth/InteractiveAuthEntryComponents.tsx +++ b/src/components/views/auth/InteractiveAuthEntryComponents.tsx @@ -220,8 +220,7 @@ export class RecaptchaAuthEntry extends React.Component { id?: string; @@ -27,9 +27,9 @@ interface IProps extends Omit { value: string; password: string; // The password we're confirming - label: string; - labelRequired: string; - labelInvalid: string; + label: TranslationKey; + labelRequired: TranslationKey; + labelInvalid: TranslationKey; onChange(ev: React.FormEvent): void; onValidate?(result: IValidationResult): void; diff --git a/src/components/views/auth/PassphraseField.tsx b/src/components/views/auth/PassphraseField.tsx index a40ba0bb07..0fdfeb3d67 100644 --- a/src/components/views/auth/PassphraseField.tsx +++ b/src/components/views/auth/PassphraseField.tsx @@ -20,7 +20,7 @@ import zxcvbn from "zxcvbn"; import SdkConfig from "../../../SdkConfig"; import withValidation, { IFieldState, IValidationResult } from "../elements/Validation"; -import { _t, _td } from "../../../languageHandler"; +import { _t, _td, TranslationKey } from "../../../languageHandler"; import Field, { IInputProps } from "../elements/Field"; import { MatrixClientPeg } from "../../../MatrixClientPeg"; @@ -34,10 +34,10 @@ interface IProps extends Omit { // Additional strings such as a username used to catch bad passwords userInputs?: string[]; - label: string; - labelEnterPassword: string; - labelStrongPassword: string; - labelAllowedButUnsafe: string; + label: TranslationKey; + labelEnterPassword: TranslationKey; + labelStrongPassword: TranslationKey; + labelAllowedButUnsafe: TranslationKey; onChange(ev: React.FormEvent): void; onValidate?(result: IValidationResult): void; diff --git a/src/components/views/context_menus/DeviceContextMenu.tsx b/src/components/views/context_menus/DeviceContextMenu.tsx index 16ac988f6f..81c33b56ef 100644 --- a/src/components/views/context_menus/DeviceContextMenu.tsx +++ b/src/components/views/context_menus/DeviceContextMenu.tsx @@ -19,9 +19,9 @@ import React, { useEffect, useState } from "react"; import MediaDeviceHandler, { MediaDeviceKindEnum } from "../../../MediaDeviceHandler"; import IconizedContextMenu, { IconizedContextMenuOptionList, IconizedContextMenuRadio } from "./IconizedContextMenu"; import { IProps as IContextMenuProps } from "../../structures/ContextMenu"; -import { _t, _td } from "../../../languageHandler"; +import { _t, _td, TranslationKey } from "../../../languageHandler"; -const SECTION_NAMES: Record = { +const SECTION_NAMES: Record = { [MediaDeviceKindEnum.AudioInput]: _td("Input devices"), [MediaDeviceKindEnum.AudioOutput]: _td("Output devices"), [MediaDeviceKindEnum.VideoInput]: _td("Cameras"), diff --git a/src/components/views/context_menus/WidgetContextMenu.tsx b/src/components/views/context_menus/WidgetContextMenu.tsx index 83fde0722c..25f5b61fe2 100644 --- a/src/components/views/context_menus/WidgetContextMenu.tsx +++ b/src/components/views/context_menus/WidgetContextMenu.tsx @@ -192,8 +192,7 @@ export const WidgetContextMenu: React.FC = ({ Modal.createDialog(QuestionDialog, { title: _t("Delete Widget"), description: _t( - "Deleting a widget removes it for all users in this room." + - " Are you sure you want to delete this widget?", + "Deleting a widget removes it for all users in this room. Are you sure you want to delete this widget?", ), button: _t("Delete widget"), onFinished: (confirmed) => { diff --git a/src/components/views/dialogs/AddExistingToSpaceDialog.tsx b/src/components/views/dialogs/AddExistingToSpaceDialog.tsx index e2d4481e0b..f3be64ea49 100644 --- a/src/components/views/dialogs/AddExistingToSpaceDialog.tsx +++ b/src/components/views/dialogs/AddExistingToSpaceDialog.tsx @@ -20,7 +20,7 @@ import { Room, EventType } from "matrix-js-sdk/src/matrix"; import { sleep } from "matrix-js-sdk/src/utils"; import { logger } from "matrix-js-sdk/src/logger"; -import { _t, _td } from "../../../languageHandler"; +import { _t, _td, TranslationKey } from "../../../languageHandler"; import BaseDialog from "./BaseDialog"; import Dropdown from "../elements/Dropdown"; import SearchBox from "../../structures/SearchBox"; @@ -357,7 +357,7 @@ export const AddExistingToSpace: React.FC = ({ }; const defaultRendererFactory = - (title: string): Renderer => + (title: TranslationKey): Renderer => (rooms, selectedToAdd, { scrollTop, height }, onChange) => (

diff --git a/src/components/views/dialogs/AnalyticsLearnMoreDialog.tsx b/src/components/views/dialogs/AnalyticsLearnMoreDialog.tsx index b3783f47ef..9e2372545c 100644 --- a/src/components/views/dialogs/AnalyticsLearnMoreDialog.tsx +++ b/src/components/views/dialogs/AnalyticsLearnMoreDialog.tsx @@ -78,9 +78,7 @@ export const AnalyticsLearnMoreDialog: React.FC = ({
{_t( - "Help us identify issues and improve %(analyticsOwner)s by sharing anonymous usage data. " + - "To understand how people use multiple devices, we'll generate a random identifier, " + - "shared by your devices.", + "Help us identify issues and improve %(analyticsOwner)s by sharing anonymous usage data. To understand how people use multiple devices, we'll generate a random identifier, shared by your devices.", { analyticsOwner }, )}
diff --git a/src/components/views/dialogs/BugReportDialog.tsx b/src/components/views/dialogs/BugReportDialog.tsx index b5db06ecdd..5a9af1dc3f 100644 --- a/src/components/views/dialogs/BugReportDialog.tsx +++ b/src/components/views/dialogs/BugReportDialog.tsx @@ -224,10 +224,7 @@ export default class BugReportDialog extends React.Component { {warning}

{_t( - "Debug logs contain application usage data including your " + - "username, the IDs or aliases of the rooms you " + - "have visited, which UI elements you last interacted with, " + - "and the usernames of other users. They do not contain messages.", + "Debug logs contain application usage data including your username, the IDs or aliases of the rooms you have visited, which UI elements you last interacted with, and the usernames of other users. They do not contain messages.", )}

@@ -273,10 +270,7 @@ export default class BugReportDialog extends React.Component { onChange={this.onTextChange} value={this.state.text} placeholder={_t( - "If there is additional context that would help in " + - "analysing the issue, such as what you were doing at " + - "the time, room IDs, user IDs, etc., " + - "please include those things here.", + "If there is additional context that would help in analysing the issue, such as what you were doing at the time, room IDs, user IDs, etc., please include those things here.", )} /> {progress} diff --git a/src/components/views/dialogs/BulkRedactDialog.tsx b/src/components/views/dialogs/BulkRedactDialog.tsx index 7e475900f6..1b3e5e786a 100644 --- a/src/components/views/dialogs/BulkRedactDialog.tsx +++ b/src/components/views/dialogs/BulkRedactDialog.tsx @@ -114,16 +114,13 @@ const BulkRedactDialog: React.FC = (props) => {

{_t( - "You are about to remove %(count)s messages by %(user)s. " + - "This will remove them permanently for everyone in the conversation. " + - "Do you wish to continue?", + "You are about to remove %(count)s messages by %(user)s. This will remove them permanently for everyone in the conversation. Do you wish to continue?", { count, user }, )}

{_t( - "For a large amount of messages, this might take some time. " + - "Please don't refresh your client in the meantime.", + "For a large amount of messages, this might take some time. Please don't refresh your client in the meantime.", )}

setKeepStateEvents(e.target.checked)}> @@ -131,8 +128,7 @@ const BulkRedactDialog: React.FC = (props) => {
{_t( - "Uncheck if you also want to remove system messages on this user " + - "(e.g. membership change, profile change…)", + "Uncheck if you also want to remove system messages on this user (e.g. membership change, profile change…)", )}
diff --git a/src/components/views/dialogs/CantStartVoiceMessageBroadcastDialog.tsx b/src/components/views/dialogs/CantStartVoiceMessageBroadcastDialog.tsx index b55598a6b5..9a76edd26c 100644 --- a/src/components/views/dialogs/CantStartVoiceMessageBroadcastDialog.tsx +++ b/src/components/views/dialogs/CantStartVoiceMessageBroadcastDialog.tsx @@ -26,8 +26,7 @@ export const createCantStartVoiceMessageBroadcastDialog = (): void => { description: (

{_t( - "You can't start a voice message as you are currently recording a live broadcast. " + - "Please end your live broadcast in order to start recording a voice message.", + "You can't start a voice message as you are currently recording a live broadcast. Please end your live broadcast in order to start recording a voice message.", )}

), diff --git a/src/components/views/dialogs/ConfirmWipeDeviceDialog.tsx b/src/components/views/dialogs/ConfirmWipeDeviceDialog.tsx index e9ab1bc3cc..fc2ab91c39 100644 --- a/src/components/views/dialogs/ConfirmWipeDeviceDialog.tsx +++ b/src/components/views/dialogs/ConfirmWipeDeviceDialog.tsx @@ -44,8 +44,7 @@ export default class ConfirmWipeDeviceDialog extends React.Component {

{_t( - "Clearing all data from this session is permanent. Encrypted messages will be lost " + - "unless their keys have been backed up.", + "Clearing all data from this session is permanent. Encrypted messages will be lost unless their keys have been backed up.", )}

diff --git a/src/components/views/dialogs/CreateRoomDialog.tsx b/src/components/views/dialogs/CreateRoomDialog.tsx index 817816913e..4b71ed6e30 100644 --- a/src/components/views/dialogs/CreateRoomDialog.tsx +++ b/src/components/views/dialogs/CreateRoomDialog.tsx @@ -311,8 +311,7 @@ export default class CreateRoomDialog extends React.Component { } } else { microcopy = _t( - "Your server admin has disabled end-to-end encryption by default " + - "in private rooms & Direct Messages.", + "Your server admin has disabled end-to-end encryption by default in private rooms & Direct Messages.", ); } e2eeSection = ( @@ -330,15 +329,13 @@ export default class CreateRoomDialog extends React.Component { } let federateLabel = _t( - "You might enable this if the room will only be used for collaborating with internal " + - "teams on your homeserver. This cannot be changed later.", + "You might enable this if the room will only be used for collaborating with internal teams on your homeserver. This cannot be changed later.", ); if (SdkConfig.get().default_federate === false) { // We only change the label if the default setting is different to avoid jarring text changes to the // user. They will have read the implications of turning this off/on, so no need to rephrase for them. federateLabel = _t( - "You might disable this if the room will be used for collaborating with external " + - "teams who have their own homeserver. This cannot be changed later.", + "You might disable this if the room will be used for collaborating with external teams who have their own homeserver. This cannot be changed later.", ); } diff --git a/src/components/views/dialogs/CryptoStoreTooNewDialog.tsx b/src/components/views/dialogs/CryptoStoreTooNewDialog.tsx index fe9f04af18..5071e3935c 100644 --- a/src/components/views/dialogs/CryptoStoreTooNewDialog.tsx +++ b/src/components/views/dialogs/CryptoStoreTooNewDialog.tsx @@ -36,9 +36,7 @@ const CryptoStoreTooNewDialog: React.FC = (props: IProps) => { Modal.createDialog(QuestionDialog, { title: _t("Sign out"), description: _t( - "To avoid losing your chat history, you must export your room keys " + - "before logging out. You will need to go back to the newer version of " + - "%(brand)s to do this", + "To avoid losing your chat history, you must export your room keys before logging out. You will need to go back to the newer version of %(brand)s to do this", { brand }, ), button: _t("Sign out"), @@ -53,9 +51,7 @@ const CryptoStoreTooNewDialog: React.FC = (props: IProps) => { }; const description = _t( - "You've previously used a newer version of %(brand)s with this session. " + - "To use this version again with end to end encryption, you will " + - "need to sign out and back in again.", + "You've previously used a newer version of %(brand)s with this session. To use this version again with end to end encryption, you will need to sign out and back in again.", { brand }, ); diff --git a/src/components/views/dialogs/DevtoolsDialog.tsx b/src/components/views/dialogs/DevtoolsDialog.tsx index 7553666425..432238e29a 100644 --- a/src/components/views/dialogs/DevtoolsDialog.tsx +++ b/src/components/views/dialogs/DevtoolsDialog.tsx @@ -17,7 +17,7 @@ limitations under the License. import React, { useState } from "react"; -import { _t, _td } from "../../../languageHandler"; +import { _t, _td, TranslationKey } from "../../../languageHandler"; import MatrixClientContext from "../../../contexts/MatrixClientContext"; import BaseDialog from "./BaseDialog"; import { TimelineEventEditor } from "./devtools/Event"; @@ -40,13 +40,13 @@ enum Category { Other, } -const categoryLabels: Record = { +const categoryLabels: Record = { [Category.Room]: _td("Room"), [Category.Other]: _td("Other"), }; export type Tool = React.FC | ((props: IDevtoolsProps) => JSX.Element); -const Tools: Record = { +const Tools: Record = { [Category.Room]: [ [_td("Send custom timeline event"), TimelineEventEditor], [_td("Explore room state"), RoomStateExplorer], diff --git a/src/components/views/dialogs/EndPollDialog.tsx b/src/components/views/dialogs/EndPollDialog.tsx index 2c81995b07..d3bfacdabe 100644 --- a/src/components/views/dialogs/EndPollDialog.tsx +++ b/src/components/views/dialogs/EndPollDialog.tsx @@ -70,9 +70,7 @@ export default class EndPollDialog extends React.Component { this.onFinished(endPoll)} diff --git a/src/components/views/dialogs/FeedbackDialog.tsx b/src/components/views/dialogs/FeedbackDialog.tsx index db2d71ceb0..916aad4e49 100644 --- a/src/components/views/dialogs/FeedbackDialog.tsx +++ b/src/components/views/dialogs/FeedbackDialog.tsx @@ -96,8 +96,7 @@ const FeedbackDialog: React.FC = (props: IProps) => { bugReports = (

{_t( - "PRO TIP: If you start a bug, please submit debug logs " + - "to help us track down the problem.", + "PRO TIP: If you start a bug, please submit debug logs to help us track down the problem.", {}, { debugLogsLink: (sub) => ( @@ -125,8 +124,7 @@ const FeedbackDialog: React.FC = (props: IProps) => {

{_t("Report a bug")}

{_t( - "Please view existing bugs on Github first. " + - "No match? Start a new one.", + "Please view existing bugs on Github first. No match? Start a new one.", {}, { existingIssuesLink: (sub) => { diff --git a/src/components/views/dialogs/IncomingSasDialog.tsx b/src/components/views/dialogs/IncomingSasDialog.tsx index 952d43ec6f..266d470823 100644 --- a/src/components/views/dialogs/IncomingSasDialog.tsx +++ b/src/components/views/dialogs/IncomingSasDialog.tsx @@ -181,17 +181,14 @@ export default class IncomingSasDialog extends React.Component { const userDetailText = [

{_t( - "Verify this user to mark them as trusted. " + - "Trusting users gives you extra peace of mind when using " + - "end-to-end encrypted messages.", + "Verify this user to mark them as trusted. Trusting users gives you extra peace of mind when using end-to-end encrypted messages.", )}

,

{_t( // NB. Below wording adjusted to singular 'session' until we have // cross-signing - "Verifying this user will mark their session as trusted, and " + - "also mark your session as trusted to them.", + "Verifying this user will mark their session as trusted, and also mark your session as trusted to them.", )}

, ]; @@ -199,15 +196,12 @@ export default class IncomingSasDialog extends React.Component { const selfDetailText = [

{_t( - "Verify this device to mark it as trusted. " + - "Trusting this device gives you and other users extra peace of mind when using " + - "end-to-end encrypted messages.", + "Verify this device to mark it as trusted. Trusting this device gives you and other users extra peace of mind when using end-to-end encrypted messages.", )}

,

{_t( - "Verifying this device will mark it as trusted, and users who have verified with " + - "you will trust this device.", + "Verifying this device will mark it as trusted, and users who have verified with you will trust this device.", )}

, ]; diff --git a/src/components/views/dialogs/IntegrationsImpossibleDialog.tsx b/src/components/views/dialogs/IntegrationsImpossibleDialog.tsx index 5b158402e8..aaeda0ce73 100644 --- a/src/components/views/dialogs/IntegrationsImpossibleDialog.tsx +++ b/src/components/views/dialogs/IntegrationsImpossibleDialog.tsx @@ -43,8 +43,7 @@ export default class IntegrationsImpossibleDialog extends React.Component

{_t( - "Your %(brand)s doesn't allow you to use an integration manager to do this. " + - "Please contact an admin.", + "Your %(brand)s doesn't allow you to use an integration manager to do this. Please contact an admin.", { brand }, )}

diff --git a/src/components/views/dialogs/InviteDialog.tsx b/src/components/views/dialogs/InviteDialog.tsx index 67818a308a..bf8113e503 100644 --- a/src/components/views/dialogs/InviteDialog.tsx +++ b/src/components/views/dialogs/InviteDialog.tsx @@ -1133,9 +1133,7 @@ export default class InviteDialog extends React.PureComponent {_t( - "Use an identity server to invite by email. " + - "Use the default (%(defaultIdentityServerName)s) " + - "or manage in Settings.", + "Use an identity server to invite by email. Use the default (%(defaultIdentityServerName)s) or manage in Settings.", { defaultIdentityServerName: abbreviateUrl(defaultIdentityServerUrl), }, @@ -1158,7 +1156,7 @@ export default class InviteDialog extends React.PureComponent {_t( - "Use an identity server to invite by email. " + "Manage in Settings.", + "Use an identity server to invite by email. Manage in Settings.", {}, { settings: (sub) => ( @@ -1349,23 +1347,21 @@ export default class InviteDialog extends React.PureComponent) or share this space.", + "Invite someone using their name, email address, username (like ) or share this space.", ); } else { helpTextUntranslated = _td( - "Invite someone using their name, username " + "(like ) or share this space.", + "Invite someone using their name, username (like ) or share this space.", ); } } else { if (identityServersEnabled) { helpTextUntranslated = _td( - "Invite someone using their name, email address, username " + - "(like ) or share this room.", + "Invite someone using their name, email address, username (like ) or share this room.", ); } else { helpTextUntranslated = _td( - "Invite someone using their name, username " + "(like ) or share this room.", + "Invite someone using their name, username (like ) or share this room.", ); } } diff --git a/src/components/views/dialogs/LazyLoadingDisabledDialog.tsx b/src/components/views/dialogs/LazyLoadingDisabledDialog.tsx index e1d5374fb3..cd69a6ce72 100644 --- a/src/components/views/dialogs/LazyLoadingDisabledDialog.tsx +++ b/src/components/views/dialogs/LazyLoadingDisabledDialog.tsx @@ -29,19 +29,14 @@ interface IProps { const LazyLoadingDisabledDialog: React.FC = (props) => { const brand = SdkConfig.get().brand; const description1 = _t( - "You've previously used %(brand)s on %(host)s with lazy loading of members enabled. " + - "In this version lazy loading is disabled. " + - "As the local cache is not compatible between these two settings, " + - "%(brand)s needs to resync your account.", + "You've previously used %(brand)s on %(host)s with lazy loading of members enabled. In this version lazy loading is disabled. As the local cache is not compatible between these two settings, %(brand)s needs to resync your account.", { brand, host: props.host, }, ); const description2 = _t( - "If the other version of %(brand)s is still open in another tab, " + - "please close it as using %(brand)s on the same host with both " + - "lazy loading enabled and disabled simultaneously will cause issues.", + "If the other version of %(brand)s is still open in another tab, please close it as using %(brand)s on the same host with both lazy loading enabled and disabled simultaneously will cause issues.", { brand, }, diff --git a/src/components/views/dialogs/LazyLoadingResyncDialog.tsx b/src/components/views/dialogs/LazyLoadingResyncDialog.tsx index c5bd2e0227..7230853167 100644 --- a/src/components/views/dialogs/LazyLoadingResyncDialog.tsx +++ b/src/components/views/dialogs/LazyLoadingResyncDialog.tsx @@ -28,9 +28,7 @@ interface IProps { const LazyLoadingResyncDialog: React.FC = (props) => { const brand = SdkConfig.get().brand; const description = _t( - "%(brand)s now uses 3-5x less memory, by only loading information " + - "about other users when needed. Please wait whilst we resynchronise " + - "with the server!", + "%(brand)s now uses 3-5x less memory, by only loading information about other users when needed. Please wait whilst we resynchronise with the server!", { brand }, ); diff --git a/src/components/views/dialogs/LeaveSpaceDialog.tsx b/src/components/views/dialogs/LeaveSpaceDialog.tsx index cc31183414..a8a9aca4d8 100644 --- a/src/components/views/dialogs/LeaveSpaceDialog.tsx +++ b/src/components/views/dialogs/LeaveSpaceDialog.tsx @@ -63,15 +63,12 @@ const LeaveSpaceDialog: React.FC = ({ space, onFinished }) => { let onlyAdminWarning; if (isOnlyAdmin(space)) { - onlyAdminWarning = _t( - "You're the only admin of this space. " + "Leaving it will mean no one has control over it.", - ); + onlyAdminWarning = _t("You're the only admin of this space. Leaving it will mean no one has control over it."); } else { const numChildrenOnlyAdminIn = roomsToLeave.filter(isOnlyAdmin).length; if (numChildrenOnlyAdminIn > 0) { onlyAdminWarning = _t( - "You're the only admin of some of the rooms or spaces you wish to leave. " + - "Leaving them will leave them without any admins.", + "You're the only admin of some of the rooms or spaces you wish to leave. Leaving them will leave them without any admins.", ); } } diff --git a/src/components/views/dialogs/LogoutDialog.tsx b/src/components/views/dialogs/LogoutDialog.tsx index 9830cf5992..add749621e 100644 --- a/src/components/views/dialogs/LogoutDialog.tsx +++ b/src/components/views/dialogs/LogoutDialog.tsx @@ -138,16 +138,12 @@ export default class LogoutDialog extends React.Component {

{_t( - "Encrypted messages are secured with end-to-end encryption. " + - "Only you and the recipient(s) have the keys to read these messages.", + "Encrypted messages are secured with end-to-end encryption. Only you and the recipient(s) have the keys to read these messages.", )}

{_t( - "When you sign out, these keys will be deleted from this device, " + - "which means you won't be able to read encrypted messages unless you " + - "have the keys for them on your other devices, or backed them up to the " + - "server.", + "When you sign out, these keys will be deleted from this device, which means you won't be able to read encrypted messages unless you have the keys for them on your other devices, or backed them up to the server.", )}

{_t("Back up your keys before signing out to avoid losing them.")}

diff --git a/src/components/views/dialogs/ManageRestrictedJoinRuleDialog.tsx b/src/components/views/dialogs/ManageRestrictedJoinRuleDialog.tsx index d29b9e8baf..e3cb4d3c2d 100644 --- a/src/components/views/dialogs/ManageRestrictedJoinRuleDialog.tsx +++ b/src/components/views/dialogs/ManageRestrictedJoinRuleDialog.tsx @@ -152,8 +152,7 @@ const ManageRestrictedJoinRuleDialog: React.FC = ({ room, selected = [], >

{_t( - "Decide which spaces can access this room. " + - "If a space is selected, its members can find and join .", + "Decide which spaces can access this room. If a space is selected, its members can find and join .", {}, { RoomName: () => {room.name}, diff --git a/src/components/views/dialogs/RegistrationEmailPromptDialog.tsx b/src/components/views/dialogs/RegistrationEmailPromptDialog.tsx index df204093e9..c6da669a91 100644 --- a/src/components/views/dialogs/RegistrationEmailPromptDialog.tsx +++ b/src/components/views/dialogs/RegistrationEmailPromptDialog.tsx @@ -59,8 +59,7 @@ const RegistrationEmailPromptDialog: React.FC = ({ onFinished }) => {

{_t( - "Just a heads up, if you don't add an email and forget your password, you could " + - "permanently lose access to your account.", + "Just a heads up, if you don't add an email and forget your password, you could permanently lose access to your account.", {}, { b: (sub) => {sub}, diff --git a/src/components/views/dialogs/ReportEventDialog.tsx b/src/components/views/dialogs/ReportEventDialog.tsx index 41a24f01bd..cd540b1b71 100644 --- a/src/components/views/dialogs/ReportEventDialog.tsx +++ b/src/components/views/dialogs/ReportEventDialog.tsx @@ -317,54 +317,39 @@ export default class ReportEventDialog extends React.Component { let subtitle: string; switch (this.state.nature) { case Nature.Disagreement: - subtitle = _t( - "What this user is writing is wrong.\n" + "This will be reported to the room moderators.", - ); + subtitle = _t("What this user is writing is wrong.\nThis will be reported to the room moderators."); break; case Nature.Toxic: subtitle = _t( - "This user is displaying toxic behaviour, " + - "for instance by insulting other users or sharing " + - "adult-only content in a family-friendly room " + - "or otherwise violating the rules of this room.\n" + - "This will be reported to the room moderators.", + "This user is displaying toxic behaviour, for instance by insulting other users or sharing adult-only content in a family-friendly room or otherwise violating the rules of this room.\nThis will be reported to the room moderators.", ); break; case Nature.Illegal: subtitle = _t( - "This user is displaying illegal behaviour, " + - "for instance by doxing people or threatening violence.\n" + - "This will be reported to the room moderators who may escalate this to legal authorities.", + "This user is displaying illegal behaviour, for instance by doxing people or threatening violence.\nThis will be reported to the room moderators who may escalate this to legal authorities.", ); break; case Nature.Spam: subtitle = _t( - "This user is spamming the room with ads, links to ads or to propaganda.\n" + - "This will be reported to the room moderators.", + "This user is spamming the room with ads, links to ads or to propaganda.\nThis will be reported to the room moderators.", ); break; case NonStandardValue.Admin: if (client.isRoomEncrypted(this.props.mxEvent.getRoomId()!)) { subtitle = _t( - "This room is dedicated to illegal or toxic content " + - "or the moderators fail to moderate illegal or toxic content.\n" + - "This will be reported to the administrators of %(homeserver)s. " + - "The administrators will NOT be able to read the encrypted content of this room.", + "This room is dedicated to illegal or toxic content or the moderators fail to moderate illegal or toxic content.\nThis will be reported to the administrators of %(homeserver)s. The administrators will NOT be able to read the encrypted content of this room.", { homeserver: homeServerName }, ); } else { subtitle = _t( - "This room is dedicated to illegal or toxic content " + - "or the moderators fail to moderate illegal or toxic content.\n" + - "This will be reported to the administrators of %(homeserver)s.", + "This room is dedicated to illegal or toxic content or the moderators fail to moderate illegal or toxic content.\nThis will be reported to the administrators of %(homeserver)s.", { homeserver: homeServerName }, ); } break; case Nature.Other: subtitle = _t( - "Any other reason. Please describe the problem.\n" + - "This will be reported to the room moderators.", + "Any other reason. Please describe the problem.\nThis will be reported to the room moderators.", ); break; default: @@ -464,10 +449,7 @@ export default class ReportEventDialog extends React.Component {

{_t( - "Reporting this message will send its unique 'event ID' to the administrator of " + - "your homeserver. If messages in this room are encrypted, your homeserver " + - "administrator will not be able to read the message text or view any files " + - "or images.", + "Reporting this message will send its unique 'event ID' to the administrator of your homeserver. If messages in this room are encrypted, your homeserver administrator will not be able to read the message text or view any files or images.", )}

{adminMessage} diff --git a/src/components/views/dialogs/RoomUpgradeDialog.tsx b/src/components/views/dialogs/RoomUpgradeDialog.tsx index ce4993c99c..fd0ff854b8 100644 --- a/src/components/views/dialogs/RoomUpgradeDialog.tsx +++ b/src/components/views/dialogs/RoomUpgradeDialog.tsx @@ -94,9 +94,7 @@ export default class RoomUpgradeDialog extends React.Component { >

{_t( - "Upgrading this room requires closing down the current " + - "instance of the room and creating a new room in its place. " + - "To give room members the best possible experience, we will:", + "Upgrading this room requires closing down the current instance of the room and creating a new room in its place. To give room members the best possible experience, we will:", )}

    @@ -104,14 +102,12 @@ export default class RoomUpgradeDialog extends React.Component {
  1. {_t("Update any local room aliases to point to the new room")}
  2. {_t( - "Stop users from speaking in the old version of the room, " + - "and post a message advising users to move to the new room", + "Stop users from speaking in the old version of the room, and post a message advising users to move to the new room", )}
  3. {_t( - "Put a link back to the old room at the start of the new room " + - "so people can see old messages", + "Put a link back to the old room at the start of the new room so people can see old messages", )}
diff --git a/src/components/views/dialogs/RoomUpgradeWarningDialog.tsx b/src/components/views/dialogs/RoomUpgradeWarningDialog.tsx index a8ad1d94a6..53c0d5bc3d 100644 --- a/src/components/views/dialogs/RoomUpgradeWarningDialog.tsx +++ b/src/components/views/dialogs/RoomUpgradeWarningDialog.tsx @@ -135,8 +135,7 @@ export default class RoomUpgradeWarningDialog extends React.Component {_t( - "This usually only affects how the room is processed on the server. If you're " + - "having problems with your %(brand)s, please report a bug.", + "This usually only affects how the room is processed on the server. If you're having problems with your %(brand)s, please report a bug.", { brand }, )}

@@ -145,8 +144,7 @@ export default class RoomUpgradeWarningDialog extends React.Component {_t( - "This usually only affects how the room is processed on the server. If you're " + - "having problems with your %(brand)s, please report a bug.", + "This usually only affects how the room is processed on the server. If you're having problems with your %(brand)s, please report a bug.", { brand, }, @@ -195,14 +193,12 @@ export default class RoomUpgradeWarningDialog extends React.Component {this.props.description || _t( - "Upgrading a room is an advanced action and is usually recommended when a room " + - "is unstable due to bugs, missing features or security vulnerabilities.", + "Upgrading a room is an advanced action and is usually recommended when a room is unstable due to bugs, missing features or security vulnerabilities.", )}

{_t( - "Please note upgrading will make a new version of the room. " + - "All current messages will stay in this archived room.", + "Please note upgrading will make a new version of the room. All current messages will stay in this archived room.", {}, { b: (sub) => {sub}, diff --git a/src/components/views/dialogs/ServerOfflineDialog.tsx b/src/components/views/dialogs/ServerOfflineDialog.tsx index 30257a701e..5de0e79eb9 100644 --- a/src/components/views/dialogs/ServerOfflineDialog.tsx +++ b/src/components/views/dialogs/ServerOfflineDialog.tsx @@ -107,8 +107,7 @@ export default class ServerOfflineDialog extends React.PureComponent {

{_t( - "Your server isn't responding to some of your requests. " + - "Below are some of the most likely reasons.", + "Your server isn't responding to some of your requests. Below are some of the most likely reasons.", )}

    diff --git a/src/components/views/dialogs/SeshatResetDialog.tsx b/src/components/views/dialogs/SeshatResetDialog.tsx index badd808ac9..c2619c2944 100644 --- a/src/components/views/dialogs/SeshatResetDialog.tsx +++ b/src/components/views/dialogs/SeshatResetDialog.tsx @@ -37,9 +37,7 @@ export default class SeshatResetDialog extends React.PureComponent { {_t("You most likely do not want to reset your event index store")}
    {_t( - "If you do, please note that none of your messages will be deleted, " + - "but the search experience might be degraded for a few moments " + - "whilst the index is recreated", + "If you do, please note that none of your messages will be deleted, but the search experience might be degraded for a few moments whilst the index is recreated", )}

diff --git a/src/components/views/dialogs/SessionRestoreErrorDialog.tsx b/src/components/views/dialogs/SessionRestoreErrorDialog.tsx index 0a82a0b8c7..950e1bb6ee 100644 --- a/src/components/views/dialogs/SessionRestoreErrorDialog.tsx +++ b/src/components/views/dialogs/SessionRestoreErrorDialog.tsx @@ -101,17 +101,14 @@ export default class SessionRestoreErrorDialog extends React.Component {

{_t( - "If you have previously used a more recent version of %(brand)s, your session " + - "may be incompatible with this version. Close this window and return " + - "to the more recent version.", + "If you have previously used a more recent version of %(brand)s, your session may be incompatible with this version. Close this window and return to the more recent version.", { brand }, )}

{_t( - "Clearing your browser's storage may fix the problem, but will sign you " + - "out and cause any encrypted chat history to become unreadable.", + "Clearing your browser's storage may fix the problem, but will sign you out and cause any encrypted chat history to become unreadable.", )}

diff --git a/src/components/views/dialogs/SetEmailDialog.tsx b/src/components/views/dialogs/SetEmailDialog.tsx index 5e903042a3..95927e4d5b 100644 --- a/src/components/views/dialogs/SetEmailDialog.tsx +++ b/src/components/views/dialogs/SetEmailDialog.tsx @@ -78,8 +78,7 @@ export default class SetEmailDialog extends React.Component { Modal.createDialog(QuestionDialog, { title: _t("Verification Pending"), description: _t( - "Please check your email and click on the link it contains. Once this " + - "is done, click continue.", + "Please check your email and click on the link it contains. Once this is done, click continue.", ), button: _t("Continue"), onFinished: this.onEmailDialogFinished, diff --git a/src/components/views/dialogs/SpacePreferencesDialog.tsx b/src/components/views/dialogs/SpacePreferencesDialog.tsx index 45f60d1449..29304bdc36 100644 --- a/src/components/views/dialogs/SpacePreferencesDialog.tsx +++ b/src/components/views/dialogs/SpacePreferencesDialog.tsx @@ -59,8 +59,7 @@ const SpacePreferencesAppearanceTab: React.FC> = ({ space {_t( - "This groups your chats with members of this space. " + - "Turning this off will hide those chats from your view of %(spaceName)s.", + "This groups your chats with members of this space. Turning this off will hide those chats from your view of %(spaceName)s.", { spaceName: space.name, }, diff --git a/src/components/views/dialogs/StorageEvictedDialog.tsx b/src/components/views/dialogs/StorageEvictedDialog.tsx index 9c81b0192c..9e749203a3 100644 --- a/src/components/views/dialogs/StorageEvictedDialog.tsx +++ b/src/components/views/dialogs/StorageEvictedDialog.tsx @@ -65,9 +65,7 @@ export default class StorageEvictedDialog extends React.Component {

{_t( - "Some session data, including encrypted message keys, is " + - "missing. Sign out and sign in to fix this, restoring keys " + - "from backup.", + "Some session data, including encrypted message keys, is missing. Sign out and sign in to fix this, restoring keys from backup.", )}

diff --git a/src/components/views/dialogs/TextInputDialog.tsx b/src/components/views/dialogs/TextInputDialog.tsx index 89b5e91640..b2a0001e6f 100644 --- a/src/components/views/dialogs/TextInputDialog.tsx +++ b/src/components/views/dialogs/TextInputDialog.tsx @@ -17,7 +17,7 @@ limitations under the License. import React, { ChangeEvent, createRef } from "react"; import Field from "../elements/Field"; -import { _t, _td } from "../../../languageHandler"; +import { _t, _td, TranslationKey } from "../../../languageHandler"; import { IFieldState, IValidationResult } from "../elements/Validation"; import BaseDialog from "./BaseDialog"; import DialogButtons from "../elements/DialogButtons"; @@ -28,7 +28,7 @@ interface IProps { value: string; placeholder?: string; button?: string; - busyMessage: string; // pass _td string + busyMessage: TranslationKey; focus: boolean; hasCancel: boolean; validator?: (fieldState: IFieldState) => Promise; // result of withValidation diff --git a/src/components/views/dialogs/UploadFailureDialog.tsx b/src/components/views/dialogs/UploadFailureDialog.tsx index 3e4860665d..1ea367969e 100644 --- a/src/components/views/dialogs/UploadFailureDialog.tsx +++ b/src/components/views/dialogs/UploadFailureDialog.tsx @@ -49,8 +49,7 @@ export default class UploadFailureDialog extends React.Component { let buttons; if (this.props.totalFiles === 1 && this.props.badFiles.length === 1) { message = _t( - "This file is too large to upload. " + - "The file size limit is %(limit)s but this file is %(sizeOfThisFile)s.", + "This file is too large to upload. The file size limit is %(limit)s but this file is %(sizeOfThisFile)s.", { limit: fileSize(this.props.contentMessages.getUploadLimit()), sizeOfThisFile: fileSize(this.props.badFiles[0].size), @@ -69,7 +68,7 @@ export default class UploadFailureDialog extends React.Component { ); } else if (this.props.totalFiles === this.props.badFiles.length) { message = _t( - "These files are too large to upload. " + "The file size limit is %(limit)s.", + "These files are too large to upload. The file size limit is %(limit)s.", { limit: fileSize(this.props.contentMessages.getUploadLimit()), }, @@ -87,7 +86,7 @@ export default class UploadFailureDialog extends React.Component { ); } else { message = _t( - "Some files are too large to be uploaded. " + "The file size limit is %(limit)s.", + "Some files are too large to be uploaded. The file size limit is %(limit)s.", { limit: fileSize(this.props.contentMessages.getUploadLimit()), }, diff --git a/src/components/views/dialogs/devtools/Event.tsx b/src/components/views/dialogs/devtools/Event.tsx index 16d76752eb..72ff739137 100644 --- a/src/components/views/dialogs/devtools/Event.tsx +++ b/src/components/views/dialogs/devtools/Event.tsx @@ -18,7 +18,7 @@ limitations under the License. import React, { ChangeEvent, ReactNode, useContext, useMemo, useRef, useState } from "react"; import { IContent, MatrixEvent } from "matrix-js-sdk/src/matrix"; -import { _t, _td } from "../../../../languageHandler"; +import { _t, _td, TranslationKey } from "../../../../languageHandler"; import Field from "../../elements/Field"; import BaseTool, { DevtoolsContext, IDevtoolsProps } from "./BaseTool"; import MatrixClientContext from "../../../../contexts/MatrixClientContext"; @@ -37,7 +37,7 @@ interface IEventEditorProps extends Pick { interface IFieldDef { id: string; - label: string; // _td + label: TranslationKey; default?: string; } diff --git a/src/components/views/dialogs/devtools/VerificationExplorer.tsx b/src/components/views/dialogs/devtools/VerificationExplorer.tsx index aaaf02dea4..9b923c5e5b 100644 --- a/src/components/views/dialogs/devtools/VerificationExplorer.tsx +++ b/src/components/views/dialogs/devtools/VerificationExplorer.tsx @@ -21,12 +21,12 @@ import { VerificationPhase as Phase, VerificationRequestEvent } from "matrix-js- import { CryptoEvent } from "matrix-js-sdk/src/crypto"; import { useTypedEventEmitter, useTypedEventEmitterState } from "../../../../hooks/useEventEmitter"; -import { _t, _td } from "../../../../languageHandler"; +import { _t, _td, TranslationKey } from "../../../../languageHandler"; import MatrixClientContext from "../../../../contexts/MatrixClientContext"; import BaseTool, { DevtoolsContext, IDevtoolsProps } from "./BaseTool"; import { Tool } from "../DevtoolsDialog"; -const PHASE_MAP: Record = { +const PHASE_MAP: Record = { [Phase.Unsent]: _td("Unsent"), [Phase.Requested]: _td("Requested"), [Phase.Ready]: _td("Ready"), diff --git a/src/components/views/dialogs/security/AccessSecretStorageDialog.tsx b/src/components/views/dialogs/security/AccessSecretStorageDialog.tsx index 2ee052d2a2..70fec9d64f 100644 --- a/src/components/views/dialogs/security/AccessSecretStorageDialog.tsx +++ b/src/components/views/dialogs/security/AccessSecretStorageDialog.tsx @@ -305,8 +305,7 @@ export default class AccessSecretStorageDialog extends React.PureComponent{_t("Only do this if you have no other device to complete verification with.")}

{_t( - "If you reset everything, you will restart with no trusted sessions, no trusted users, and " + - "might not be able to see past messages.", + "If you reset everything, you will restart with no trusted sessions, no trusted users, and might not be able to see past messages.", )}

{"\uD83D\uDC4E "} {_t( - "Unable to access secret storage. " + - "Please verify that you entered the correct Security Phrase.", + "Unable to access secret storage. Please verify that you entered the correct Security Phrase.", )}
); diff --git a/src/components/views/dialogs/security/ConfirmDestroyCrossSigningDialog.tsx b/src/components/views/dialogs/security/ConfirmDestroyCrossSigningDialog.tsx index 3b3d76b74c..5e95e5ba72 100644 --- a/src/components/views/dialogs/security/ConfirmDestroyCrossSigningDialog.tsx +++ b/src/components/views/dialogs/security/ConfirmDestroyCrossSigningDialog.tsx @@ -44,10 +44,7 @@ export default class ConfirmDestroyCrossSigningDialog extends React.Component

{_t( - "Deleting cross-signing keys is permanent. " + - "Anyone you have verified with will see security alerts. " + - "You almost certainly don't want to do this, unless " + - "you've lost every device you can cross-sign from.", + "Deleting cross-signing keys is permanent. Anyone you have verified with will see security alerts. You almost certainly don't want to do this, unless you've lost every device you can cross-sign from.", )}

diff --git a/src/components/views/dialogs/security/RestoreKeyBackupDialog.tsx b/src/components/views/dialogs/security/RestoreKeyBackupDialog.tsx index fdf558e8ea..ca2f01da08 100644 --- a/src/components/views/dialogs/security/RestoreKeyBackupDialog.tsx +++ b/src/components/views/dialogs/security/RestoreKeyBackupDialog.tsx @@ -351,8 +351,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent

{_t( - "Backup could not be decrypted with this Security Key: " + - "please verify that you entered the correct Security Key.", + "Backup could not be decrypted with this Security Key: please verify that you entered the correct Security Key.", )}

@@ -363,8 +362,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent

{_t( - "Backup could not be decrypted with this Security Phrase: " + - "please verify that you entered the correct Security Phrase.", + "Backup could not be decrypted with this Security Phrase: please verify that you entered the correct Security Phrase.", )}

@@ -418,8 +416,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent

{_t( - "Access your secure message history and set up secure " + - "messaging by entering your Security Phrase.", + "Access your secure message history and set up secure messaging by entering your Security Phrase.", )}

@@ -441,9 +438,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent {_t( - "If you've forgotten your Security Phrase you can " + - "use your Security Key or " + - "set up new recovery options", + "If you've forgotten your Security Phrase you can use your Security Key or set up new recovery options", {}, { button1: (s) => ( @@ -493,8 +488,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent

{_t( - "Access your secure message history and set up secure " + - "messaging by entering your Security Key.", + "Access your secure message history and set up secure messaging by entering your Security Key.", )}

@@ -516,8 +510,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent
{_t( - "If you've forgotten your Security Key you can " + - "", + "If you've forgotten your Security Key you can ", {}, { button: (s) => ( diff --git a/src/components/views/dialogs/spotlight/SpotlightDialog.tsx b/src/components/views/dialogs/spotlight/SpotlightDialog.tsx index 6f787e80e9..58d90fa977 100644 --- a/src/components/views/dialogs/spotlight/SpotlightDialog.tsx +++ b/src/components/views/dialogs/spotlight/SpotlightDialog.tsx @@ -923,10 +923,7 @@ const SpotlightDialog: React.FC = ({ initialText = "", initialFilter = n

{_t("Some results may be hidden")}

- {_t( - "If you can't find the room you're looking for, " + - "ask for an invite or create a new room.", - )} + {_t("If you can't find the room you're looking for, ask for an invite or create a new room.")}
); diff --git a/src/components/views/location/EnableLiveShare.tsx b/src/components/views/location/EnableLiveShare.tsx index 2f432ccad0..95243427fd 100644 --- a/src/components/views/location/EnableLiveShare.tsx +++ b/src/components/views/location/EnableLiveShare.tsx @@ -36,10 +36,7 @@ export const EnableLiveShare: React.FC = ({ onSubmit }) => {

{_t( - "Please note: this is a labs feature using a temporary implementation. " + - "This means you will not be able to delete your location history, " + - "and advanced users will be able to see your location history " + - "even after you stop sharing your live location with this room.", + "Please note: this is a labs feature using a temporary implementation. This means you will not be able to delete your location history, and advanced users will be able to see your location history even after you stop sharing your live location with this room.", )}

{ let submitDebugLogsContent: JSX.Element = <>; if (err instanceof ConnectionError) { friendlyErrorMessage = _t( - "A network error occurred while trying to find and jump to the given date. " + - "Your homeserver might be down or there was just a temporary problem with " + - "your internet connection. Please try again. If this continues, please " + - "contact your homeserver administrator.", + "A network error occurred while trying to find and jump to the given date. Your homeserver might be down or there was just a temporary problem with your internet connection. Please try again. If this continues, please contact your homeserver administrator.", ); } else if (err instanceof MatrixError) { if (err?.errcode === "M_NOT_FOUND") { friendlyErrorMessage = _t( - "We were unable to find an event looking forwards from %(dateString)s. " + - "Try choosing an earlier date.", + "We were unable to find an event looking forwards from %(dateString)s. Try choosing an earlier date.", { dateString: formatFullDateNoDay(new Date(unixTimestamp)) }, ); } else { @@ -192,8 +188,7 @@ export default class DateSeparator extends React.Component { submitDebugLogsContent = (

{_t( - "Please submit debug logs to help us " + - "track down the problem.", + "Please submit debug logs to help us track down the problem.", {}, { debugLogsLink: (sub) => ( diff --git a/src/components/views/messages/DownloadActionButton.tsx b/src/components/views/messages/DownloadActionButton.tsx index 8e974c1e15..ca13f64e9e 100644 --- a/src/components/views/messages/DownloadActionButton.tsx +++ b/src/components/views/messages/DownloadActionButton.tsx @@ -22,7 +22,7 @@ import { Icon as DownloadIcon } from "../../../../res/img/download.svg"; import { MediaEventHelper } from "../../../utils/MediaEventHelper"; import { RovingAccessibleTooltipButton } from "../../../accessibility/RovingTabIndex"; import Spinner from "../elements/Spinner"; -import { _t, _td } from "../../../languageHandler"; +import { _t, _td, TranslationKey } from "../../../languageHandler"; import { FileDownloader } from "../../../utils/FileDownloader"; interface IProps { @@ -37,7 +37,7 @@ interface IProps { interface IState { loading: boolean; blob?: Blob; - tooltip: string; + tooltip: TranslationKey; } export default class DownloadActionButton extends React.PureComponent { diff --git a/src/components/views/messages/EncryptionEvent.tsx b/src/components/views/messages/EncryptionEvent.tsx index bcd6136ec9..0131faba4e 100644 --- a/src/components/views/messages/EncryptionEvent.tsx +++ b/src/components/views/messages/EncryptionEvent.tsx @@ -53,16 +53,14 @@ const EncryptionEvent = forwardRef(({ mxEvent, timestamp } else if (dmPartner) { const displayName = room?.getMember(dmPartner)?.rawDisplayName || dmPartner; subtitle = _t( - "Messages here are end-to-end encrypted. " + - "Verify %(displayName)s in their profile - tap on their profile picture.", + "Messages here are end-to-end encrypted. Verify %(displayName)s in their profile - tap on their profile picture.", { displayName }, ); } else if (room && isLocalRoom(room)) { subtitle = _t("Messages in this chat will be end-to-end encrypted."); } else { subtitle = _t( - "Messages in this room are end-to-end encrypted. " + - "When people join, you can verify them in their profile, just tap on their profile picture.", + "Messages in this room are end-to-end encrypted. When people join, you can verify them in their profile, just tap on their profile picture.", ); } diff --git a/src/components/views/messages/RoomPredecessorTile.tsx b/src/components/views/messages/RoomPredecessorTile.tsx index 2275bcbcca..61bce205f8 100644 --- a/src/components/views/messages/RoomPredecessorTile.tsx +++ b/src/components/views/messages/RoomPredecessorTile.tsx @@ -106,9 +106,7 @@ export const RoomPredecessorTile: React.FC = ({ mxEvent, timestamp }) => {!!guessedLink ? ( <> {_t( - "Can't find the old version of this room (room ID: %(roomId)s), and we have not been " + - "provided with 'via_servers' to look for it. It's possible that guessing the " + - "server from the room ID will work. If you want to try, click this link:", + "Can't find the old version of this room (room ID: %(roomId)s), and we have not been provided with 'via_servers' to look for it. It's possible that guessing the server from the room ID will work. If you want to try, click this link:", { roomId: predecessor.roomId, }, @@ -117,8 +115,7 @@ export const RoomPredecessorTile: React.FC = ({ mxEvent, timestamp }) => ) : ( _t( - "Can't find the old version of this room (room ID: %(roomId)s), and we have not been " + - "provided with 'via_servers' to look for it.", + "Can't find the old version of this room (room ID: %(roomId)s), and we have not been provided with 'via_servers' to look for it.", { roomId: predecessor.roomId, }, diff --git a/src/components/views/messages/TextualBody.tsx b/src/components/views/messages/TextualBody.tsx index 88ed8adcd8..8dcbde7ede 100644 --- a/src/components/views/messages/TextualBody.tsx +++ b/src/components/views/messages/TextualBody.tsx @@ -494,9 +494,7 @@ export default class TextualBody extends React.Component { description: (

{_t( - "You are about to be taken to a third-party site so you can " + - "authenticate your account for use with %(integrationsUrl)s. " + - "Do you wish to continue?", + "You are about to be taken to a third-party site so you can authenticate your account for use with %(integrationsUrl)s. Do you wish to continue?", { integrationsUrl: integrationsUrl }, )}
diff --git a/src/components/views/right_panel/EncryptionInfo.tsx b/src/components/views/right_panel/EncryptionInfo.tsx index 029c969ec3..6247dd4d50 100644 --- a/src/components/views/right_panel/EncryptionInfo.tsx +++ b/src/components/views/right_panel/EncryptionInfo.tsx @@ -81,8 +81,7 @@ const EncryptionInfo: React.FC = ({

{_t("Messages in this room are end-to-end encrypted.")}

{_t( - "Your messages are secured and only you and the recipient have " + - "the unique keys to unlock them.", + "Your messages are secured and only you and the recipient have the unique keys to unlock them.", )}

@@ -93,8 +92,7 @@ const EncryptionInfo: React.FC = ({

{_t("Messages in this room are not end-to-end encrypted.")}

{_t( - "In encrypted rooms, your messages are secured and only you and the recipient have " + - "the unique keys to unlock them.", + "In encrypted rooms, your messages are secured and only you and the recipient have the unique keys to unlock them.", )}

diff --git a/src/components/views/right_panel/PinnedMessagesCard.tsx b/src/components/views/right_panel/PinnedMessagesCard.tsx index 04a384a04b..6f178be786 100644 --- a/src/components/views/right_panel/PinnedMessagesCard.tsx +++ b/src/components/views/right_panel/PinnedMessagesCard.tsx @@ -181,8 +181,7 @@ const PinnedMessagesCard: React.FC = ({ room, onClose, permalinkCreator {_t("Nothing pinned, yet")} {_t( - "If you have permissions, open the menu on any message and select " + - "Pin to stick them here.", + "If you have permissions, open the menu on any message and select Pin to stick them here.", {}, { b: (sub) => {sub}, diff --git a/src/components/views/right_panel/UserInfo.tsx b/src/components/views/right_panel/UserInfo.tsx index b75d7f8719..ac22365b6a 100644 --- a/src/components/views/right_panel/UserInfo.tsx +++ b/src/components/views/right_panel/UserInfo.tsx @@ -383,8 +383,7 @@ export const UserOptionsSection: React.FC<{ description: (
{_t( - "All messages and invites from this user will be hidden. " + - "Are you sure you want to ignore them?", + "All messages and invites from this user will be hidden. Are you sure you want to ignore them?", )}
), @@ -523,14 +522,10 @@ export const warnSelfDemote = async (isSpace: boolean): Promise => {
{isSpace ? _t( - "You will not be able to undo this change as you are demoting yourself, " + - "if you are the last privileged user in the space it will be impossible " + - "to regain privileges.", + "You will not be able to undo this change as you are demoting yourself, if you are the last privileged user in the space it will be impossible to regain privileges.", ) : _t( - "You will not be able to undo this change as you are demoting yourself, " + - "if you are the last privileged user in the room it will be impossible " + - "to regain privileges.", + "You will not be able to undo this change as you are demoting yourself, if you are the last privileged user in the room it will be impossible to regain privileges.", )}
), @@ -1185,8 +1180,7 @@ export const PowerLevelEditor: React.FC<{ description: (
{_t( - "You will not be able to undo this change as you are promoting the user " + - "to have the same power level as yourself.", + "You will not be able to undo this change as you are promoting the user to have the same power level as yourself.", )}
{_t("Are you sure?")} @@ -1355,9 +1349,7 @@ const BasicUserInfo: React.FC<{ description: (
{_t( - "Deactivating this user will log them out and prevent them from logging back in. Additionally, " + - "they will leave all the rooms they are in. This action cannot be reversed. Are you sure you " + - "want to deactivate this user?", + "Deactivating this user will log them out and prevent them from logging back in. Additionally, they will leave all the rooms they are in. This action cannot be reversed. Are you sure you want to deactivate this user?", )}
), diff --git a/src/components/views/right_panel/VerificationPanel.tsx b/src/components/views/right_panel/VerificationPanel.tsx index 718c5cde4f..dce0ba0f13 100644 --- a/src/components/views/right_panel/VerificationPanel.tsx +++ b/src/components/views/right_panel/VerificationPanel.tsx @@ -93,9 +93,7 @@ export default class VerificationPanel extends React.PureComponent {_t( - "The device you are trying to verify doesn't support scanning a " + - "QR code or emoji verification, which is what %(brand)s supports. Try " + - "with a different client.", + "The device you are trying to verify doesn't support scanning a QR code or emoji verification, which is what %(brand)s supports. Try with a different client.", { brand }, )}

diff --git a/src/components/views/room_settings/AliasSettings.tsx b/src/components/views/room_settings/AliasSettings.tsx index f1729b7596..9fcb0614fc 100644 --- a/src/components/views/room_settings/AliasSettings.tsx +++ b/src/components/views/room_settings/AliasSettings.tsx @@ -182,8 +182,7 @@ export default class AliasSettings extends React.Component { Modal.createDialog(ErrorDialog, { title: _t("Error updating main address"), description: _t( - "There was an error updating the room's main address. It may not be allowed by the server " + - "or a temporary failure occurred.", + "There was an error updating the room's main address. It may not be allowed by the server or a temporary failure occurred.", ), }); this.setState({ canonicalAlias: oldAlias }); @@ -222,8 +221,7 @@ export default class AliasSettings extends React.Component { Modal.createDialog(ErrorDialog, { title: _t("Error updating main address"), description: _t( - "There was an error updating the room's alternative addresses. " + - "It may not be allowed by the server or a temporary failure occurred.", + "There was an error updating the room's alternative addresses. It may not be allowed by the server or a temporary failure occurred.", ), }); }) @@ -258,8 +256,7 @@ export default class AliasSettings extends React.Component { Modal.createDialog(ErrorDialog, { title: _t("Error creating address"), description: _t( - "There was an error creating that address. It may not be allowed by the server " + - "or a temporary failure occurred.", + "There was an error creating that address. It may not be allowed by the server or a temporary failure occurred.", ), }); }); @@ -286,8 +283,7 @@ export default class AliasSettings extends React.Component { description = _t("You don't have permission to delete the address."); } else { description = _t( - "There was an error removing that address. It may no longer exist or a temporary " + - "error occurred.", + "There was an error removing that address. It may no longer exist or a temporary error occurred.", ); } Modal.createDialog(ErrorDialog, { @@ -450,13 +446,11 @@ export default class AliasSettings extends React.Component { description={ isSpaceRoom ? _t( - "Set addresses for this space so users can find this space " + - "through your homeserver (%(localDomain)s)", + "Set addresses for this space so users can find this space through your homeserver (%(localDomain)s)", { localDomain }, ) : _t( - "Set addresses for this room so users can find this room " + - "through your homeserver (%(localDomain)s)", + "Set addresses for this room so users can find this room through your homeserver (%(localDomain)s)", { localDomain }, ) } diff --git a/src/components/views/room_settings/UrlPreviewSettings.tsx b/src/components/views/room_settings/UrlPreviewSettings.tsx index d0314320f1..a04d61994a 100644 --- a/src/components/views/room_settings/UrlPreviewSettings.tsx +++ b/src/components/views/room_settings/UrlPreviewSettings.tsx @@ -95,9 +95,7 @@ export default class UrlPreviewSettings extends React.Component { } } else { previewsForAccount = _t( - "In encrypted rooms, like this one, URL previews are disabled by default to ensure that your " + - "homeserver (where the previews are generated) cannot gather information about links you see in " + - "this room.", + "In encrypted rooms, like this one, URL previews are disabled by default to ensure that your homeserver (where the previews are generated) cannot gather information about links you see in this room.", ); } @@ -114,8 +112,7 @@ export default class UrlPreviewSettings extends React.Component { <>

{_t( - "When someone puts a URL in their message, a URL preview can be shown to give more " + - "information about that link such as the title, description, and an image from the website.", + "When someone puts a URL in their message, a URL preview can be shown to give more information about that link such as the title, description, and an image from the website.", )}

{previewsForAccount}

diff --git a/src/components/views/rooms/E2EIcon.tsx b/src/components/views/rooms/E2EIcon.tsx index 01a6b8a498..3e6e0ce91a 100644 --- a/src/components/views/rooms/E2EIcon.tsx +++ b/src/components/views/rooms/E2EIcon.tsx @@ -18,7 +18,7 @@ limitations under the License. import React, { CSSProperties, useState } from "react"; import classNames from "classnames"; -import { _t, _td } from "../../../languageHandler"; +import { _t, _td, TranslationKey } from "../../../languageHandler"; import AccessibleButton from "../elements/AccessibleButton"; import Tooltip, { Alignment } from "../elements/Tooltip"; import { E2EStatus } from "../../../utils/ShieldUtils"; @@ -32,12 +32,12 @@ export enum E2EState { Unauthenticated = "unauthenticated", } -const crossSigningUserTitles: { [key in E2EState]?: string } = { +const crossSigningUserTitles: { [key in E2EState]?: TranslationKey } = { [E2EState.Warning]: _td("This user has not verified all of their sessions."), [E2EState.Normal]: _td("You have not verified this user."), [E2EState.Verified]: _td("You have verified this user. This user has verified all of their sessions."), }; -const crossSigningRoomTitles: { [key in E2EState]?: string } = { +const crossSigningRoomTitles: { [key in E2EState]?: TranslationKey } = { [E2EState.Warning]: _td("Someone is using an unknown session"), [E2EState.Normal]: _td("This room is end-to-end encrypted"), [E2EState.Verified]: _td("Everyone in this room is verified"), @@ -85,7 +85,7 @@ const E2EIcon: React.FC> = ({ className, ); - let e2eTitle: string | undefined; + let e2eTitle: TranslationKey | undefined; if (isUser) { e2eTitle = crossSigningUserTitles[status]; } else { diff --git a/src/components/views/rooms/EntityTile.tsx b/src/components/views/rooms/EntityTile.tsx index 647ea82751..c3596aeadf 100644 --- a/src/components/views/rooms/EntityTile.tsx +++ b/src/components/views/rooms/EntityTile.tsx @@ -20,7 +20,7 @@ import React from "react"; import classNames from "classnames"; import AccessibleButton from "../elements/AccessibleButton"; -import { _t, _td } from "../../../languageHandler"; +import { _t, _td, TranslationKey } from "../../../languageHandler"; import E2EIcon, { E2EState } from "./E2EIcon"; import BaseAvatar from "../avatars/BaseAvatar"; import PresenceLabel from "./PresenceLabel"; @@ -30,7 +30,7 @@ export enum PowerStatus { Moderator = "moderator", } -const PowerLabel: Record = { +const PowerLabel: Record = { [PowerStatus.Admin]: _td("Admin"), [PowerStatus.Moderator]: _td("Mod"), }; diff --git a/src/components/views/rooms/NewRoomIntro.tsx b/src/components/views/rooms/NewRoomIntro.tsx index e03576b07a..66fd9a1bbf 100644 --- a/src/components/views/rooms/NewRoomIntro.tsx +++ b/src/components/views/rooms/NewRoomIntro.tsx @@ -20,7 +20,7 @@ import { EventType, Room, User, MatrixClient } from "matrix-js-sdk/src/matrix"; import MatrixClientContext from "../../../contexts/MatrixClientContext"; import RoomContext from "../../../contexts/RoomContext"; import DMRoomMap from "../../../utils/DMRoomMap"; -import { _t, _td } from "../../../languageHandler"; +import { _t, _td, TranslationKey } from "../../../languageHandler"; import AccessibleButton, { ButtonEvent } from "../elements/AccessibleButton"; import MiniAvatarUploader, { AVATAR_SIZE } from "../elements/MiniAvatarUploader"; import RoomAvatar from "../avatars/RoomAvatar"; @@ -44,7 +44,7 @@ function hasExpectedEncryptionSettings(matrixClient: MatrixClient, room: Room): return isPublic || !privateShouldBeEncrypted(matrixClient) || isEncrypted; } -const determineIntroMessage = (room: Room, encryptedSingle3rdPartyInvite: boolean): string => { +const determineIntroMessage = (room: Room, encryptedSingle3rdPartyInvite: boolean): TranslationKey => { if (room instanceof LocalRoom) { return _td("Send your first message to invite to chat"); } @@ -270,9 +270,7 @@ const NewRoomIntro: React.FC = () => { } const subText = _t( - "Your private messages are normally encrypted, but this room isn't. " + - "Usually this is due to an unsupported device or method being used, " + - "like email invites.", + "Your private messages are normally encrypted, but this room isn't. Usually this is due to an unsupported device or method being used, like email invites.", ); let subButton: JSX.Element | undefined; diff --git a/src/components/views/rooms/RoomList.tsx b/src/components/views/rooms/RoomList.tsx index 64d2c73926..61e4a4ff0a 100644 --- a/src/components/views/rooms/RoomList.tsx +++ b/src/components/views/rooms/RoomList.tsx @@ -26,7 +26,7 @@ import { ActionPayload } from "../../../dispatcher/payloads"; import { ViewRoomDeltaPayload } from "../../../dispatcher/payloads/ViewRoomDeltaPayload"; import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload"; import { useEventEmitterState } from "../../../hooks/useEventEmitter"; -import { _t, _td } from "../../../languageHandler"; +import { _t, _td, TranslationKey } from "../../../languageHandler"; import { MatrixClientPeg } from "../../../MatrixClientPeg"; import PosthogTrackers from "../../../PosthogTrackers"; import SettingsStore from "../../../settings/SettingsStore"; @@ -93,7 +93,7 @@ export const TAG_ORDER: TagID[] = [ const ALWAYS_VISIBLE_TAGS: TagID[] = [DefaultTagID.DM, DefaultTagID.Untagged]; interface ITagAesthetics { - sectionLabel: string; + sectionLabel: TranslationKey; sectionLabelRaw?: string; AuxButtonComponent?: ComponentType; isInvite: boolean; diff --git a/src/components/views/rooms/RoomPreviewBar.tsx b/src/components/views/rooms/RoomPreviewBar.tsx index 613833bd66..46c0738f3a 100644 --- a/src/components/views/rooms/RoomPreviewBar.tsx +++ b/src/components/views/rooms/RoomPreviewBar.tsx @@ -421,8 +421,7 @@ export default class RoomPreviewBar extends React.Component { } const joinRule = this.joinRule(); const errCodeMessage = _t( - "An error (%(errcode)s) was returned while trying to validate your " + - "invite. You could try to pass this information on to the person who invited you.", + "An error (%(errcode)s) was returned while trying to validate your invite. You could try to pass this information on to the person who invited you.", { errcode: this.state.threePidFetchError?.errcode || _t("unknown error code") }, ); switch (joinRule) { @@ -447,8 +446,7 @@ export default class RoomPreviewBar extends React.Component { case MessageCase.InvitedEmailNotFoundInAccount: { if (roomName) { title = _t( - "This invite to %(roomName)s was sent to %(email)s which is not " + - "associated with your account", + "This invite to %(roomName)s was sent to %(email)s which is not associated with your account", { roomName, email: this.props.invitedEmail, @@ -585,9 +583,7 @@ export default class RoomPreviewBar extends React.Component { subTitle = [ _t("Try again later, or ask a room or space admin to check if you have access."), _t( - "%(errcode)s was returned while trying to access the room or space. " + - "If you think you're seeing this message in error, please " + - "submit a bug report.", + "%(errcode)s was returned while trying to access the room or space. If you think you're seeing this message in error, please submit a bug report.", { errcode: String(this.props.error?.errcode) }, { issueLink: (label) => ( diff --git a/src/components/views/rooms/RoomUpgradeWarningBar.tsx b/src/components/views/rooms/RoomUpgradeWarningBar.tsx index a63dc26f27..b637a57fe5 100644 --- a/src/components/views/rooms/RoomUpgradeWarningBar.tsx +++ b/src/components/views/rooms/RoomUpgradeWarningBar.tsx @@ -73,15 +73,12 @@ export default class RoomUpgradeWarningBar extends React.PureComponent

{_t( - "Upgrading this room will shut down the current instance of the room and create " + - "an upgraded room with the same name.", + "Upgrading this room will shut down the current instance of the room and create an upgraded room with the same name.", )}

{_t( - "Warning: upgrading a room will not automatically migrate room members " + - "to the new version of the room. We'll post a link to the new room in the old " + - "version of the room - room members will have to click this link to join the new room.", + "Warning: upgrading a room will not automatically migrate room members to the new version of the room. We'll post a link to the new room in the old version of the room - room members will have to click this link to join the new room.", {}, { b: (sub) => {sub}, @@ -111,8 +108,7 @@ export default class RoomUpgradeWarningBar extends React.PureComponent

{_t( - "This room is running room version , which this homeserver has " + - "marked as unstable.", + "This room is running room version , which this homeserver has marked as unstable.", {}, { roomVersion: () => {this.props.room.getVersion()}, diff --git a/src/components/views/rooms/SendMessageComposer.tsx b/src/components/views/rooms/SendMessageComposer.tsx index 1c401df30b..14d09fd83d 100644 --- a/src/components/views/rooms/SendMessageComposer.tsx +++ b/src/components/views/rooms/SendMessageComposer.tsx @@ -496,7 +496,10 @@ export class SendMessageComposer extends React.Component { this.sendVisibilityToWidget(this.props.isStickerPickerOpen); } - private imError(errorMsg: string, e: Error): void { + private imError(errorMsg: TranslationKey, e: Error): void { logger.error(errorMsg, e); this.setState({ imError: _t(errorMsg), diff --git a/src/components/views/rooms/ThirdPartyMemberInfo.tsx b/src/components/views/rooms/ThirdPartyMemberInfo.tsx index 0ffd0dc551..b8682e2751 100644 --- a/src/components/views/rooms/ThirdPartyMemberInfo.tsx +++ b/src/components/views/rooms/ThirdPartyMemberInfo.tsx @@ -108,8 +108,7 @@ export default class ThirdPartyMemberInfo extends React.Component { summarisedStatus = ( {_t( - "Your account has a cross-signing identity in secret storage, " + - "but it is not yet trusted by this session.", + "Your account has a cross-signing identity in secret storage, but it is not yet trusted by this session.", )} ); diff --git a/src/components/views/settings/EventIndexPanel.tsx b/src/components/views/settings/EventIndexPanel.tsx index e3cbed3679..215c141cb5 100644 --- a/src/components/views/settings/EventIndexPanel.tsx +++ b/src/components/views/settings/EventIndexPanel.tsx @@ -149,8 +149,7 @@ export default class EventIndexPanel extends React.Component<{}, IState> { <> {_t( - "Securely cache encrypted messages locally for them " + - "to appear in search results, using %(size)s to store messages from %(rooms)s rooms.", + "Securely cache encrypted messages locally for them to appear in search results, using %(size)s to store messages from %(rooms)s rooms.", { size: formatBytes(this.state.eventIndexSize, 0), // This drives the singular / plural string @@ -188,10 +187,7 @@ export default class EventIndexPanel extends React.Component<{}, IState> { eventIndexingSettings = ( {_t( - "%(brand)s is missing some components required for securely " + - "caching encrypted messages locally. If you'd like to " + - "experiment with this feature, build a custom %(brand)s Desktop " + - "with search components added.", + "%(brand)s is missing some components required for securely caching encrypted messages locally. If you'd like to experiment with this feature, build a custom %(brand)s Desktop with search components added.", { brand, }, @@ -209,9 +205,7 @@ export default class EventIndexPanel extends React.Component<{}, IState> { eventIndexingSettings = ( {_t( - "%(brand)s can't securely cache encrypted messages locally " + - "while running in a web browser. Use %(brand)s Desktop " + - "for encrypted messages to appear in search results.", + "%(brand)s can't securely cache encrypted messages locally while running in a web browser. Use %(brand)s Desktop for encrypted messages to appear in search results.", { brand, }, diff --git a/src/components/views/settings/JoinRuleSettings.tsx b/src/components/views/settings/JoinRuleSettings.tsx index 3c29b33842..b6a1e797f5 100644 --- a/src/components/views/settings/JoinRuleSettings.tsx +++ b/src/components/views/settings/JoinRuleSettings.tsx @@ -323,9 +323,7 @@ const JoinRuleSettings: React.FC = ({ warning = ( {_t( - "This room is in some spaces you're not an admin of. " + - "In those spaces, the old room will still be shown, " + - "but people will be prompted to join the new one.", + "This room is in some spaces you're not an admin of. In those spaces, the old room will still be shown, but people will be prompted to join the new one.", )} ); @@ -335,8 +333,7 @@ const JoinRuleSettings: React.FC = ({ targetVersion, <> {_t( - "This upgrade will allow members of selected spaces " + - "access to this room without an invite.", + "This upgrade will allow members of selected spaces access to this room without an invite.", )} {warning} , diff --git a/src/components/views/settings/SecureBackupPanel.tsx b/src/components/views/settings/SecureBackupPanel.tsx index 2f866aa183..9ed62f6fc1 100644 --- a/src/components/views/settings/SecureBackupPanel.tsx +++ b/src/components/views/settings/SecureBackupPanel.tsx @@ -259,17 +259,14 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> { <> {_t( - "This session is not backing up your keys, " + - "but you do have an existing backup you can restore from " + - "and add to going forward.", + "This session is not backing up your keys, but you do have an existing backup you can restore from and add to going forward.", {}, { b: (sub) => {sub} }, )} {_t( - "Connect this session to key backup before signing out to avoid " + - "losing any keys that may only be on this session.", + "Connect this session to key backup before signing out to avoid losing any keys that may only be on this session.", )} @@ -382,9 +379,7 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> { <> {_t( - "Back up your encryption keys with your account data in case you " + - "lose access to your sessions. Your keys will be secured with a " + - "unique Security Key.", + "Back up your encryption keys with your account data in case you lose access to your sessions. Your keys will be secured with a unique Security Key.", )} {statusDescription} diff --git a/src/components/views/settings/SetIdServer.tsx b/src/components/views/settings/SetIdServer.tsx index 2256556b58..10494e3fd6 100644 --- a/src/components/views/settings/SetIdServer.tsx +++ b/src/components/views/settings/SetIdServer.tsx @@ -296,9 +296,7 @@ export default class SetIdServer extends React.Component {

{_t( - "You should remove your personal data from identity server " + - " before disconnecting. Unfortunately, identity server " + - " is currently offline or cannot be reached.", + "You should remove your personal data from identity server before disconnecting. Unfortunately, identity server is currently offline or cannot be reached.", {}, messageElements, )} @@ -307,8 +305,7 @@ export default class SetIdServer extends React.Component {

  • {_t( - "check your browser plugins for anything that might block " + - "the identity server (such as Privacy Badger)", + "check your browser plugins for anything that might block the identity server (such as Privacy Badger)", )}
  • @@ -338,8 +335,7 @@ export default class SetIdServer extends React.Component {

    {_t( - "We recommend that you remove your email addresses and phone numbers " + - "from the identity server before disconnecting.", + "We recommend that you remove your email addresses and phone numbers from the identity server before disconnecting.", )}

@@ -388,15 +384,13 @@ export default class SetIdServer extends React.Component { if (idServerUrl) { sectionTitle = _t("Identity server (%(server)s)", { server: abbreviateUrl(idServerUrl) }); bodyText = _t( - "You are currently using to discover and be discoverable by " + - "existing contacts you know. You can change your identity server below.", + "You are currently using to discover and be discoverable by existing contacts you know. You can change your identity server below.", {}, { server: (sub) => {abbreviateUrl(idServerUrl)} }, ); if (this.props.missingTerms) { bodyText = _t( - "If you don't want to use to discover and be discoverable by existing " + - "contacts you know, enter another identity server below.", + "If you don't want to use to discover and be discoverable by existing contacts you know, enter another identity server below.", {}, { server: (sub) => {abbreviateUrl(idServerUrl)} }, ); @@ -404,9 +398,7 @@ export default class SetIdServer extends React.Component { } else { sectionTitle = _t("Identity server"); bodyText = _t( - "You are not currently using an identity server. " + - "To discover and be discoverable by existing contacts you know, " + - "add one below.", + "You are not currently using an identity server. To discover and be discoverable by existing contacts you know, add one below.", ); } @@ -414,15 +406,11 @@ export default class SetIdServer extends React.Component { if (idServerUrl) { let discoButtonContent: React.ReactNode = _t("Disconnect"); let discoBodyText = _t( - "Disconnecting from your identity server will mean you " + - "won't be discoverable by other users and you won't be " + - "able to invite others by email or phone.", + "Disconnecting from your identity server will mean you won't be discoverable by other users and you won't be able to invite others by email or phone.", ); if (this.props.missingTerms) { discoBodyText = _t( - "Using an identity server is optional. If you choose not to " + - "use an identity server, you won't be discoverable by other users " + - "and you won't be able to invite others by email or phone.", + "Using an identity server is optional. If you choose not to use an identity server, you won't be discoverable by other users and you won't be able to invite others by email or phone.", ); discoButtonContent = _t("Do not use an identity server"); } diff --git a/src/components/views/settings/SetIntegrationManager.tsx b/src/components/views/settings/SetIntegrationManager.tsx index 0d8767befa..0e1edb2e45 100644 --- a/src/components/views/settings/SetIntegrationManager.tsx +++ b/src/components/views/settings/SetIntegrationManager.tsx @@ -63,7 +63,7 @@ export default class SetIntegrationManager extends React.Component(%(serverName)s) to manage bots, widgets, " + "and sticker packs.", + "Use an integration manager (%(serverName)s) to manage bots, widgets, and sticker packs.", { serverName: currentManager.name }, { b: (sub) => {sub} }, ); @@ -92,8 +92,7 @@ export default class SetIntegrationManager extends React.Component{bodyText}
{_t( - "Integration managers receive configuration data, and can modify widgets, " + - "send room invites, and set power levels on your behalf.", + "Integration managers receive configuration data, and can modify widgets, send room invites, and set power levels on your behalf.", )} diff --git a/src/components/views/settings/account/PhoneNumbers.tsx b/src/components/views/settings/account/PhoneNumbers.tsx index 4bbdcb2c06..e3fde056bb 100644 --- a/src/components/views/settings/account/PhoneNumbers.tsx +++ b/src/components/views/settings/account/PhoneNumbers.tsx @@ -280,8 +280,7 @@ export default class PhoneNumbers extends React.Component {
{_t( - "A text message has been sent to +%(msisdn)s. " + - "Please enter the verification code it contains.", + "A text message has been sent to +%(msisdn)s. Please enter the verification code it contains.", { msisdn: msisdn }, )}
diff --git a/src/components/views/settings/devices/DeviceDetailHeading.tsx b/src/components/views/settings/devices/DeviceDetailHeading.tsx index cc338d634b..895da52d2e 100644 --- a/src/components/views/settings/devices/DeviceDetailHeading.tsx +++ b/src/components/views/settings/devices/DeviceDetailHeading.tsx @@ -84,14 +84,12 @@ const DeviceNameEditor: React.FC void }> = ({ devic <>

{_t( - `Other users in direct messages and rooms that you join ` + - `are able to view a full list of your sessions.`, + "Other users in direct messages and rooms that you join are able to view a full list of your sessions.", )}

{_t( - `This provides them with confidence that they are really speaking to you, ` + - `but it also means they can see the session name you enter here.`, + "This provides them with confidence that they are really speaking to you, but it also means they can see the session name you enter here.", )}

diff --git a/src/components/views/settings/devices/DeviceSecurityLearnMore.tsx b/src/components/views/settings/devices/DeviceSecurityLearnMore.tsx index 03ce6fec06..647501bc1e 100644 --- a/src/components/views/settings/devices/DeviceSecurityLearnMore.tsx +++ b/src/components/views/settings/devices/DeviceSecurityLearnMore.tsx @@ -42,8 +42,7 @@ const securityCardContent: Record<

{_t( - `This means that you have all the keys needed to unlock your encrypted messages ` + - `and confirm to other users that you trust this session.`, + "This means that you have all the keys needed to unlock your encrypted messages and confirm to other users that you trust this session.", )}

@@ -60,8 +59,7 @@ const securityCardContent: Record<

{_t( - `You should make especially certain that you recognise these sessions ` + - `as they could represent an unauthorised use of your account.`, + "You should make especially certain that you recognise these sessions as they could represent an unauthorised use of your account.", )}

@@ -98,8 +96,7 @@ const securityCardContent: Record<

{_t( - `Removing inactive sessions improves security and performance, ` + - `and makes it easier for you to identify if a new session is suspicious.`, + "Removing inactive sessions improves security and performance, and makes it easier for you to identify if a new session is suspicious.", )}

diff --git a/src/components/views/settings/devices/FilteredDeviceList.tsx b/src/components/views/settings/devices/FilteredDeviceList.tsx index 76d157d233..805e62a54a 100644 --- a/src/components/views/settings/devices/FilteredDeviceList.tsx +++ b/src/components/views/settings/devices/FilteredDeviceList.tsx @@ -88,8 +88,7 @@ const securityCardContent: Record< [DeviceSecurityVariation.Unverified]: { title: _t("Unverified sessions"), description: _t( - `Verify your sessions for enhanced secure messaging or ` + - `sign out from those you don't recognize or use anymore.`, + "Verify your sessions for enhanced secure messaging or sign out from those you don't recognize or use anymore.", ), }, [DeviceSecurityVariation.Unverifiable]: { @@ -99,7 +98,7 @@ const securityCardContent: Record< [DeviceSecurityVariation.Inactive]: { title: _t("Inactive sessions"), description: _t( - `Consider signing out from old sessions ` + `(%(inactiveAgeDays)s days or older) you don't use anymore.`, + "Consider signing out from old sessions (%(inactiveAgeDays)s days or older) you don't use anymore.", { inactiveAgeDays: INACTIVE_DEVICE_AGE_DAYS }, ), }, diff --git a/src/components/views/settings/devices/LoginWithQRSection.tsx b/src/components/views/settings/devices/LoginWithQRSection.tsx index 1e0ddf70c2..eb8882bf35 100644 --- a/src/components/views/settings/devices/LoginWithQRSection.tsx +++ b/src/components/views/settings/devices/LoginWithQRSection.tsx @@ -56,8 +56,7 @@ export default class LoginWithQRSection extends React.Component {

{_t( - "You can use this device to sign in a new device with a QR code. You will need to " + - "scan the QR code shown on this device with your device that's signed out.", + "You can use this device to sign in a new device with a QR code. You will need to scan the QR code shown on this device with your device that's signed out.", )}

diff --git a/src/components/views/settings/devices/SecurityRecommendations.tsx b/src/components/views/settings/devices/SecurityRecommendations.tsx index 2805f0e7cb..a9b848e3f4 100644 --- a/src/components/views/settings/devices/SecurityRecommendations.tsx +++ b/src/components/views/settings/devices/SecurityRecommendations.tsx @@ -63,8 +63,7 @@ const SecurityRecommendations: React.FC = ({ devices, currentDeviceId, go description={ <> {_t( - `Verify your sessions for enhanced secure messaging` + - ` or sign out from those you don't recognize or use anymore.`, + "Verify your sessions for enhanced secure messaging or sign out from those you don't recognize or use anymore.", )} @@ -88,8 +87,7 @@ const SecurityRecommendations: React.FC = ({ devices, currentDeviceId, go description={ <> {_t( - `Consider signing out from old sessions ` + - `(%(inactiveAgeDays)s days or older) you don't use anymore.`, + "Consider signing out from old sessions (%(inactiveAgeDays)s days or older) you don't use anymore.", { inactiveAgeDays }, )} diff --git a/src/components/views/settings/notifications/NotificationSettings2.tsx b/src/components/views/settings/notifications/NotificationSettings2.tsx index 657783fae3..d6fc97a3d4 100644 --- a/src/components/views/settings/notifications/NotificationSettings2.tsx +++ b/src/components/views/settings/notifications/NotificationSettings2.tsx @@ -110,11 +110,7 @@ export default function NotificationSettings2(): JSX.Element { onAction={() => reconcile(model!)} > {_t( - "Update:" + - "We’ve simplified Notifications Settings to make options easier to find. " + - "Some custom settings you’ve chosen in the past are not shown here, but they’re still active. " + - "If you proceed, some of your settings may change. " + - "Learn more", + "Update:We’ve simplified Notifications Settings to make options easier to find. Some custom settings you’ve chosen in the past are not shown here, but they’re still active. If you proceed, some of your settings may change. Learn more", {}, { strong: boldText, diff --git a/src/components/views/settings/tabs/room/AdvancedRoomSettingsTab.tsx b/src/components/views/settings/tabs/room/AdvancedRoomSettingsTab.tsx index f45f0bb5f2..85479ca45b 100644 --- a/src/components/views/settings/tabs/room/AdvancedRoomSettingsTab.tsx +++ b/src/components/views/settings/tabs/room/AdvancedRoomSettingsTab.tsx @@ -120,9 +120,7 @@ export default class AdvancedRoomSettingsTab extends React.Component

{_t( - "Warning: upgrading a room will not automatically migrate room members " + - "to the new version of the room. We'll post a link to the new room in the old " + - "version of the room - room members will have to click this link to join the new room.", + "Warning: upgrading a room will not automatically migrate room members to the new version of the room. We'll post a link to the new room in the old version of the room - room members will have to click this link to join the new room.", {}, { b: (sub) => {sub}, diff --git a/src/components/views/settings/tabs/room/BridgeSettingsTab.tsx b/src/components/views/settings/tabs/room/BridgeSettingsTab.tsx index 03c67604eb..03d5208902 100644 --- a/src/components/views/settings/tabs/room/BridgeSettingsTab.tsx +++ b/src/components/views/settings/tabs/room/BridgeSettingsTab.tsx @@ -63,7 +63,7 @@ export default class BridgeSettingsTab extends React.Component {

{_t( - "This room is bridging messages to the following platforms. " + "Learn more.", + "This room is bridging messages to the following platforms. Learn more.", {}, { // TODO: We don't have this link yet: this will prevent the translators @@ -85,7 +85,7 @@ export default class BridgeSettingsTab extends React.Component { content = (

{_t( - "This room isn't bridging messages to any platforms. " + "Learn more.", + "This room isn't bridging messages to any platforms. Learn more.", {}, { // TODO: We don't have this link yet: this will prevent the translators diff --git a/src/components/views/settings/tabs/room/NotificationSettingsTab.tsx b/src/components/views/settings/tabs/room/NotificationSettingsTab.tsx index 21d13dfd63..0f3d258dec 100644 --- a/src/components/views/settings/tabs/room/NotificationSettingsTab.tsx +++ b/src/components/views/settings/tabs/room/NotificationSettingsTab.tsx @@ -221,8 +221,7 @@ export default class NotificationsSettingsTab extends React.Component {_t( - "Get notified only with mentions and keywords " + - "as set up in your settings", + "Get notified only with mentions and keywords as set up in your settings", {}, { a: (sub) => ( diff --git a/src/components/views/settings/tabs/room/RolesRoomSettingsTab.tsx b/src/components/views/settings/tabs/room/RolesRoomSettingsTab.tsx index 645eaf216f..d43f0faa79 100644 --- a/src/components/views/settings/tabs/room/RolesRoomSettingsTab.tsx +++ b/src/components/views/settings/tabs/room/RolesRoomSettingsTab.tsx @@ -20,7 +20,7 @@ import { logger } from "matrix-js-sdk/src/logger"; import { throttle, get } from "lodash"; import { compare } from "matrix-js-sdk/src/utils"; -import { _t, _td } from "../../../../../languageHandler"; +import { _t, _td, TranslationKey } from "../../../../../languageHandler"; import AccessibleButton from "../../../elements/AccessibleButton"; import Modal from "../../../../../Modal"; import ErrorDialog from "../../../dialogs/ErrorDialog"; @@ -207,8 +207,7 @@ export default class RolesRoomSettingsTab extends React.Component { Modal.createDialog(ErrorDialog, { title: _t("Error changing power level requirement"), description: _t( - "An error occurred changing the room's power level requirements. Ensure you have sufficient " + - "permissions and try again.", + "An error occurred changing the room's power level requirements. Ensure you have sufficient permissions and try again.", ), }); }); @@ -233,8 +232,7 @@ export default class RolesRoomSettingsTab extends React.Component { Modal.createDialog(ErrorDialog, { title: _t("Error changing power level"), description: _t( - "An error occurred changing the user's power level. Ensure you have sufficient " + - "permissions and try again.", + "An error occurred changing the user's power level. Ensure you have sufficient permissions and try again.", ), }); }); @@ -249,7 +247,7 @@ export default class RolesRoomSettingsTab extends React.Component { const plContent = plEvent ? plEvent.getContent() || {} : {}; const canChangeLevels = room.currentState.mayClientSendStateEvent(EventType.RoomPowerLevels, client); - const plEventsToLabels: Record = { + const plEventsToLabels: Record = { // These will be translated for us later. [EventType.RoomAvatar]: isSpaceRoom ? _td("Change space avatar") : _td("Change room avatar"), [EventType.RoomName]: isSpaceRoom ? _td("Change space name") : _td("Change room name"), @@ -458,10 +456,11 @@ export default class RolesRoomSettingsTab extends React.Component { return null; } - let label = plEventsToLabels[eventType]; - if (label) { + const translationKeyForEvent = plEventsToLabels[eventType]; + let label: string; + if (translationKeyForEvent) { const brand = SdkConfig.get("element_call").brand ?? DEFAULTS.element_call.brand; - label = _t(label, { brand }); + label = _t(translationKeyForEvent, { brand }); } else { label = _t("Send %(eventType)s events", { eventType }); } diff --git a/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.tsx b/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.tsx index 354d2abc44..83d9094e50 100644 --- a/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.tsx +++ b/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.tsx @@ -122,11 +122,7 @@ export default class SecurityRoomSettingsTab extends React.Component {" "} {_t( - "It's not recommended to add encryption to public rooms. " + - "Anyone can find and join public rooms, so anyone can read messages in them. " + - "You'll get none of the benefits of encryption, and you won't be able to turn it " + - "off later. Encrypting messages in a public room will make receiving and sending " + - "messages slower.", + "It's not recommended to add encryption to public rooms. Anyone can find and join public rooms, so anyone can read messages in them. You'll get none of the benefits of encryption, and you won't be able to turn it off later. Encrypting messages in a public room will make receiving and sending messages slower.", undefined, { b: (sub) => {sub} }, )}{" "} @@ -134,8 +130,7 @@ export default class SecurityRoomSettingsTab extends React.Component {" "} {_t( - "To avoid these issues, create a new encrypted room for " + - "the conversation you plan to have.", + "To avoid these issues, create a new encrypted room for the conversation you plan to have.", undefined, { a: (sub) => ( @@ -165,9 +160,7 @@ export default class SecurityRoomSettingsTab extends React.ComponentLearn more about encryption.", + "Once enabled, encryption for a room cannot be disabled. Messages sent in an encrypted room cannot be seen by the server, only by the participants of the room. Enabling encryption may prevent many bots and bridges from working correctly. Learn more about encryption.", {}, { a: (sub) => {sub}, @@ -322,10 +315,7 @@ export default class SecurityRoomSettingsTab extends React.Component {" "} {_t( - "It's not recommended to make encrypted rooms public. " + - "It will mean anyone can find and join the room, so anyone can read messages. " + - "You'll get none of the benefits of encryption. Encrypting messages in a public " + - "room will make receiving and sending messages slower.", + "It's not recommended to make encrypted rooms public. It will mean anyone can find and join the room, so anyone can read messages. You'll get none of the benefits of encryption. Encrypting messages in a public room will make receiving and sending messages slower.", undefined, { b: (sub) => {sub} }, )}{" "} @@ -333,8 +323,7 @@ export default class SecurityRoomSettingsTab extends React.Component {" "} {_t( - "To avoid these issues, create a new public room for the conversation " + - "you plan to have.", + "To avoid these issues, create a new public room for the conversation you plan to have.", undefined, { a: (sub) => ( @@ -398,8 +387,7 @@ export default class SecurityRoomSettingsTab extends React.Component

{_t( - "People with supported clients will be able to join " + - "the room without having a registered account.", + "People with supported clients will be able to join the room without having a registered account.", )}

diff --git a/src/components/views/settings/tabs/room/VoipRoomSettingsTab.tsx b/src/components/views/settings/tabs/room/VoipRoomSettingsTab.tsx index f1704f6fe4..212a18364f 100644 --- a/src/components/views/settings/tabs/room/VoipRoomSettingsTab.tsx +++ b/src/components/views/settings/tabs/room/VoipRoomSettingsTab.tsx @@ -82,10 +82,9 @@ const ElementCallSwitch: React.FC = ({ room }) => { {_t( - "Agree to the identity server (%(serverName)s) Terms of Service to " + - "allow yourself to be discoverable by email address or phone number.", + "Agree to the identity server (%(serverName)s) Terms of Service to allow yourself to be discoverable by email address or phone number.", { serverName: this.state.idServerName }, )} diff --git a/src/components/views/settings/tabs/user/HelpUserSettingsTab.tsx b/src/components/views/settings/tabs/user/HelpUserSettingsTab.tsx index e66e15bf5b..35afc3c95b 100644 --- a/src/components/views/settings/tabs/user/HelpUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/HelpUserSettingsTab.tsx @@ -136,8 +136,7 @@ export default class HelpUserSettingsTab extends React.Component
  • {_t( - "The default cover photo is © " + - "Jesús Roncero used under the terms of CC-BY-SA 4.0.", + "The default cover photo is © Jesús Roncero used under the terms of CC-BY-SA 4.0.", {}, { photo: (sub) => ( @@ -166,8 +165,7 @@ export default class HelpUserSettingsTab extends React.Component
  • {_t( - "The twemoji-colr font is © Mozilla Foundation " + - "used under the terms of Apache 2.0.", + "The twemoji-colr font is © Mozilla Foundation used under the terms of Apache 2.0.", {}, { colr: (sub) => ( @@ -194,9 +192,7 @@ export default class HelpUserSettingsTab extends React.Component
  • {_t( - "The Twemoji emoji art is © " + - "Twitter, Inc and other contributors used under the terms of " + - "CC-BY 4.0.", + "The Twemoji emoji art is © Twitter, Inc and other contributors used under the terms of CC-BY 4.0.", {}, { twemoji: (sub) => ( @@ -244,8 +240,7 @@ export default class HelpUserSettingsTab extends React.Component faqText = (
    {_t( - "For help with using %(brand)s, click here or start a chat with our " + - "bot using the button below.", + "For help with using %(brand)s, click here or start a chat with our bot using the button below.", { brand, }, @@ -284,16 +279,11 @@ export default class HelpUserSettingsTab extends React.Component <> {_t( - "If you've submitted a bug via GitHub, debug logs can help " + - "us track down the problem. ", + "If you've submitted a bug via GitHub, debug logs can help us track down the problem. ", )} {_t( - "Debug logs contain application " + - "usage data including your username, the IDs or aliases of " + - "the rooms you have visited, which UI elements you " + - "last interacted with, and the usernames of other users. " + - "They do not contain messages.", + "Debug logs contain application usage data including your username, the IDs or aliases of the rooms you have visited, which UI elements you last interacted with, and the usernames of other users. They do not contain messages.", )} } @@ -303,8 +293,7 @@ export default class HelpUserSettingsTab extends React.Component {_t( - "To report a Matrix-related security issue, please read the Matrix.org " + - "Security Disclosure Policy.", + "To report a Matrix-related security issue, please read the Matrix.org Security Disclosure Policy.", {}, { a: (sub) => ( @@ -369,8 +358,7 @@ export default class HelpUserSettingsTab extends React.Component {_t("Access Token")} {_t( - "Your access token gives full access to your account." + - " Do not share it with anyone.", + "Your access token gives full access to your account. Do not share it with anyone.", )} this.context.getAccessToken()}> diff --git a/src/components/views/settings/tabs/user/LabsUserSettingsTab.tsx b/src/components/views/settings/tabs/user/LabsUserSettingsTab.tsx index da6f614780..d1f7393018 100644 --- a/src/components/views/settings/tabs/user/LabsUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/LabsUserSettingsTab.tsx @@ -113,9 +113,7 @@ export default class LabsUserSettingsTab extends React.Component<{}> { {_t( - "What's next for %(brand)s? " + - "Labs are the best way to get things early, " + - "test out new features and help shape them before they actually launch.", + "What's next for %(brand)s? Labs are the best way to get things early, test out new features and help shape them before they actually launch.", { brand: SdkConfig.get("brand") }, )} @@ -126,11 +124,7 @@ export default class LabsUserSettingsTab extends React.Component<{}> { {_t( - "Feeling experimental? " + - "Try out our latest ideas in development. " + - "These features are not finalised; " + - "they may be unstable, may change, or may be dropped altogether. " + - "Learn more.", + "Feeling experimental? Try out our latest ideas in development. These features are not finalised; they may be unstable, may change, or may be dropped altogether. Learn more.", {}, { a: (sub) => { diff --git a/src/components/views/settings/tabs/user/MjolnirUserSettingsTab.tsx b/src/components/views/settings/tabs/user/MjolnirUserSettingsTab.tsx index 92981407fb..0bb2d8e9a2 100644 --- a/src/components/views/settings/tabs/user/MjolnirUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/MjolnirUserSettingsTab.tsx @@ -259,28 +259,21 @@ export default class MjolnirUserSettingsTab extends React.Component<{}, IState> {_t("⚠ These settings are meant for advanced users.")}

    {_t( - "Add users and servers you want to ignore here. Use asterisks " + - "to have %(brand)s match any characters. For example, @bot:* " + - "would ignore all users that have the name 'bot' on any server.", + "Add users and servers you want to ignore here. Use asterisks to have %(brand)s match any characters. For example, @bot:* would ignore all users that have the name 'bot' on any server.", { brand }, { code: (s) => {s} }, )}

    {_t( - "Ignoring people is done through ban lists which contain rules for " + - "who to ban. Subscribing to a ban list means the users/servers blocked by " + - "that list will be hidden from you.", + "Ignoring people is done through ban lists which contain rules for who to ban. Subscribing to a ban list means the users/servers blocked by that list will be hidden from you.", )}

    {_t( - "Your server admin has disabled end-to-end encryption by default " + - "in private rooms & Direct Messages.", + "Your server admin has disabled end-to-end encryption by default in private rooms & Direct Messages.", )}
    ); diff --git a/src/components/views/settings/tabs/user/SessionManagerTab.tsx b/src/components/views/settings/tabs/user/SessionManagerTab.tsx index ff20814f6c..56b852db8d 100644 --- a/src/components/views/settings/tabs/user/SessionManagerTab.tsx +++ b/src/components/views/settings/tabs/user/SessionManagerTab.tsx @@ -303,8 +303,7 @@ const SessionManagerTab: React.FC = () => { /> } description={_t( - `For best security, verify your sessions and sign out ` + - `from any session that you don't recognize or use anymore.`, + "For best security, verify your sessions and sign out from any session that you don't recognize or use anymore.", )} data-testid="other-sessions-section" stretchContent diff --git a/src/components/views/settings/tabs/user/SidebarUserSettingsTab.tsx b/src/components/views/settings/tabs/user/SidebarUserSettingsTab.tsx index 3c9aaffe55..16734ccf16 100644 --- a/src/components/views/settings/tabs/user/SidebarUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/SidebarUserSettingsTab.tsx @@ -68,8 +68,7 @@ const SidebarUserSettingsTab: React.FC = () => { {_t("Create a space")}

    {_t( - "Spaces are a new way to group rooms and people. What kind of Space do you want to create? " + - "You can change this later.", + "Spaces are a new way to group rooms and people. What kind of Space do you want to create? You can change this later.", )}

    diff --git a/src/components/views/user-onboarding/UserOnboardingHeader.tsx b/src/components/views/user-onboarding/UserOnboardingHeader.tsx index 977674c5b8..0080202245 100644 --- a/src/components/views/user-onboarding/UserOnboardingHeader.tsx +++ b/src/components/views/user-onboarding/UserOnboardingHeader.tsx @@ -43,8 +43,7 @@ export function UserOnboardingHeader({ useCase }: Props): JSX.Element { case UseCase.PersonalMessaging: title = _t("Secure messaging for friends and family"); description = _t( - "With free end-to-end encrypted messaging, and unlimited voice and video calls, " + - "%(brand)s is a great way to stay in touch.", + "With free end-to-end encrypted messaging, and unlimited voice and video calls, %(brand)s is a great way to stay in touch.", { brand: SdkConfig.get("brand"), }, @@ -55,8 +54,7 @@ export function UserOnboardingHeader({ useCase }: Props): JSX.Element { case UseCase.WorkMessaging: title = _t("Secure messaging for work"); description = _t( - "With free end-to-end encrypted messaging, and unlimited voice and video calls," + - " %(brand)s is a great way to stay in touch.", + "With free end-to-end encrypted messaging, and unlimited voice and video calls, %(brand)s is a great way to stay in touch.", { brand: SdkConfig.get("brand"), }, @@ -67,8 +65,7 @@ export function UserOnboardingHeader({ useCase }: Props): JSX.Element { case UseCase.CommunityMessaging: title = _t("Community ownership"); description = _t( - "Keep ownership and control of community discussion.\n" + - "Scale to support millions, with powerful moderation and interoperability.", + "Keep ownership and control of community discussion.\nScale to support millions, with powerful moderation and interoperability.", ); image = require("../../../../res/img/user-onboarding/CommunityMessaging.png"); actionLabel = _t("Find your people"); @@ -78,8 +75,7 @@ export function UserOnboardingHeader({ useCase }: Props): JSX.Element { brand: SdkConfig.get("brand"), }); description = _t( - "With free end-to-end encrypted messaging, and unlimited voice and video calls," + - " %(brand)s is a great way to stay in touch.", + "With free end-to-end encrypted messaging, and unlimited voice and video calls, %(brand)s is a great way to stay in touch.", { brand: SdkConfig.get("brand"), }, diff --git a/src/components/views/verification/VerificationComplete.tsx b/src/components/views/verification/VerificationComplete.tsx index 1f034b4741..6e203e9900 100644 --- a/src/components/views/verification/VerificationComplete.tsx +++ b/src/components/views/verification/VerificationComplete.tsx @@ -31,8 +31,7 @@ export default class VerificationComplete extends React.Component {

    {_t("You've successfully verified this user.")}

    {_t( - "Secure messages with this user are end-to-end encrypted and not able to be " + - "read by third parties.", + "Secure messages with this user are end-to-end encrypted and not able to be read by third parties.", )}

    {

    {_t("Unrecognised command: %(commandText)s", { commandText })}

    {_t( - "You can use /help to list available commands. " + - "Did you mean to send this as a message?", + "You can use /help to list available commands. Did you mean to send this as a message?", {}, { code: (t) => {t}, diff --git a/src/effects/effect.ts b/src/effects/effect.ts index fe194bbde3..5bddafe902 100644 --- a/src/effects/effect.ts +++ b/src/effects/effect.ts @@ -15,6 +15,8 @@ limitations under the License. */ +import { TranslationKey } from "../languageHandler"; + export type Effect = { /** * one or more emojis that will trigger this effect @@ -29,9 +31,9 @@ export type Effect = { */ command: string; /** - * a function that returns the translated description of the effect + * a function that returns the translatable description of the effect */ - description: () => string; + description: () => TranslationKey; /** * a function that returns the translated fallback message. this message will be shown if the user did not provide a custom message */ diff --git a/src/languageHandler.tsx b/src/languageHandler.tsx index ca2ec105b6..8f77cc4565 100644 --- a/src/languageHandler.tsx +++ b/src/languageHandler.tsx @@ -23,12 +23,14 @@ import { logger } from "matrix-js-sdk/src/logger"; import { Optional } from "matrix-events-sdk"; import { MapWithDefault, safeSet } from "matrix-js-sdk/src/utils"; +import type Translations from "./i18n/strings/en_EN.json"; import SettingsStore from "./settings/SettingsStore"; import PlatformPeg from "./PlatformPeg"; import { SettingLevel } from "./settings/SettingLevel"; import { retry } from "./utils/promise"; import SdkConfig from "./SdkConfig"; import { ModuleRunner } from "./modules/ModuleRunner"; +import { Leaves } from "./@types/common"; // @ts-ignore - $webapp is a webpack resolve alias pointing to the output directory, see webpack config import webpackLangJsonUrl from "$webapp/i18n/languages.json"; @@ -46,7 +48,7 @@ counterpart.setSeparator("|"); const FALLBACK_LOCALE = "en"; counterpart.setFallbackLocale(FALLBACK_LOCALE); -interface ErrorOptions { +export interface ErrorOptions { // Because we're mixing the subsitution variables and `cause` into the same object // below, we want them to always explicitly say whether there is an underlying error // or not to avoid typos of "cause" slipping through unnoticed. @@ -72,7 +74,7 @@ interface ErrorOptions { export class UserFriendlyError extends Error { public readonly translatedMessage: string; - public constructor(message: string, substitutionVariablesAndCause?: IVariables & ErrorOptions) { + public constructor(message: TranslationKey, substitutionVariablesAndCause?: IVariables & ErrorOptions) { const errorOptions = { cause: substitutionVariablesAndCause?.cause, }; @@ -100,10 +102,20 @@ export function getUserLanguage(): string { } } +/** + * A type representing the union of possible keys into the translation file using `|` delimiter to access nested fields. + * @example `common|error` to access `error` within the `common` sub-object. + * { + * "common": { + * "error": "Error" + * } + * } + */ +export type TranslationKey = Leaves; + // Function which only purpose is to mark that a string is translatable // Does not actually do anything. It's helpful for automatic extraction of translatable strings -export function _td(s: string): string { - // eslint-disable-line @typescript-eslint/naming-convention +export function _td(s: TranslationKey): TranslationKey { return s; } @@ -176,6 +188,9 @@ function safeCounterpartTranslate(text: string, variables?: IVariables): { trans return translateWithFallback(text, options); } +/** + * The value a variable or tag can take for a translation interpolation. + */ type SubstitutionValue = number | string | React.ReactNode | ((sub: string) => React.ReactNode); export interface IVariables { @@ -189,7 +204,7 @@ export type TranslatedString = string | React.ReactNode; // For development/testing purposes it is useful to also output the original string // Don't do that for release versions -const annotateStrings = (result: TranslatedString, translationKey: string): TranslatedString => { +const annotateStrings = (result: TranslatedString, translationKey: TranslationKey): TranslatedString => { if (!ANNOTATE_STRINGS) { return result; } @@ -222,9 +237,9 @@ const annotateStrings = (result: TranslatedString, translationKey: string): Tran * @return a React component if any non-strings were used in substitutions, otherwise a string */ // eslint-next-line @typescript-eslint/naming-convention -export function _t(text: string, variables?: IVariables): string; -export function _t(text: string, variables: IVariables | undefined, tags: Tags): React.ReactNode; -export function _t(text: string, variables?: IVariables, tags?: Tags): TranslatedString { +export function _t(text: TranslationKey, variables?: IVariables): string; +export function _t(text: TranslationKey, variables: IVariables | undefined, tags: Tags): React.ReactNode; +export function _t(text: TranslationKey, variables?: IVariables, tags?: Tags): TranslatedString { // The translation returns text so there's no XSS vector here (no unsafe HTML, no code execution) const { translated } = safeCounterpartTranslate(text, variables); const substituted = substitute(translated, variables, tags); @@ -243,9 +258,9 @@ export function _t(text: string, variables?: IVariables, tags?: Tags): Translate * or translation used a fallback locale, otherwise a string */ // eslint-next-line @typescript-eslint/naming-convention -export function _tDom(text: string, variables?: IVariables): TranslatedString; -export function _tDom(text: string, variables: IVariables, tags: Tags): React.ReactNode; -export function _tDom(text: string, variables?: IVariables, tags?: Tags): TranslatedString { +export function _tDom(text: TranslationKey, variables?: IVariables): TranslatedString; +export function _tDom(text: TranslationKey, variables: IVariables, tags: Tags): React.ReactNode; +export function _tDom(text: TranslationKey, variables?: IVariables, tags?: Tags): TranslatedString { // The translation returns text so there's no XSS vector here (no unsafe HTML, no code execution) const { translated, isFallback } = safeCounterpartTranslate(text, variables); const substituted = substitute(translated, variables, tags); diff --git a/src/modules/ProxiedModuleApi.ts b/src/modules/ProxiedModuleApi.ts index e7d7cb4b44..e3aa453a8a 100644 --- a/src/modules/ProxiedModuleApi.ts +++ b/src/modules/ProxiedModuleApi.ts @@ -25,7 +25,7 @@ import { IRegisterRequestParams } from "matrix-js-sdk/src/matrix"; import { ModuleUiDialogOptions } from "@matrix-org/react-sdk-module-api/lib/types/ModuleUiDialogOptions"; import Modal from "../Modal"; -import { _t } from "../languageHandler"; +import { _t, TranslationKey } from "../languageHandler"; import { ModuleUiDialog } from "../components/views/dialogs/ModuleUiDialog"; import SdkConfig from "../SdkConfig"; import PlatformPeg from "../PlatformPeg"; @@ -74,7 +74,7 @@ export class ProxiedModuleApi implements ModuleApi { /** * @override */ - public translateString(s: string, variables?: Record): string { + public translateString(s: TranslationKey, variables?: Record): string { return _t(s, variables); } diff --git a/src/notifications/VectorPushRulesDefinitions.ts b/src/notifications/VectorPushRulesDefinitions.ts index b9a9b33411..37c6b7a60f 100644 --- a/src/notifications/VectorPushRulesDefinitions.ts +++ b/src/notifications/VectorPushRulesDefinitions.ts @@ -17,7 +17,7 @@ limitations under the License. import { IAnnotatedPushRule, PushRuleAction, RuleId } from "matrix-js-sdk/src/matrix"; import { logger } from "matrix-js-sdk/src/logger"; -import { _td } from "../languageHandler"; +import { _td, TranslationKey } from "../languageHandler"; import { StandardActions } from "./StandardActions"; import { PushRuleVectorState, VectorState } from "./PushRuleVectorState"; import { NotificationUtils } from "./NotificationUtils"; @@ -27,7 +27,7 @@ type StateToActionsMap = { }; interface IVectorPushRuleDefinition { - description: string; + description: TranslationKey; vectorStateToActions: StateToActionsMap; /** * Rules that should be updated to be kept in sync @@ -37,7 +37,7 @@ interface IVectorPushRuleDefinition { } class VectorPushRuleDefinition { - public readonly description: string; + public readonly description: TranslationKey; public readonly vectorStateToActions: StateToActionsMap; public readonly syncedRuleIds?: (RuleId | string)[]; diff --git a/src/phonenumber.ts b/src/phonenumber.ts index 682e87d379..64150e6f50 100644 --- a/src/phonenumber.ts +++ b/src/phonenumber.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { _td } from "./languageHandler"; +import { _td, TranslationKey } from "./languageHandler"; const PHONE_NUMBER_REGEXP = /^[0-9 -.]+$/; @@ -44,7 +44,7 @@ export const getEmojiFlag = (countryCode: string): string => { export interface PhoneNumberCountryDefinition { iso2: string; - name: string; + name: TranslationKey; prefix: string; } diff --git a/src/settings/Settings.tsx b/src/settings/Settings.tsx index 7592c21209..cd976bb1ef 100644 --- a/src/settings/Settings.tsx +++ b/src/settings/Settings.tsx @@ -18,7 +18,7 @@ limitations under the License. import { MatrixClient } from "matrix-js-sdk/src/matrix"; import React, { ReactNode } from "react"; -import { _t, _td } from "../languageHandler"; +import { _t, _td, TranslationKey } from "../languageHandler"; import { NotificationBodyEnabledController, NotificationsEnabledController, @@ -98,7 +98,7 @@ export enum Features { OidcNativeFlow = "feature_oidc_native_flow", } -export const labGroupNames: Record = { +export const labGroupNames: Record = { [LabGroup.Messaging]: _td("Messaging"), [LabGroup.Profile]: _td("Profile"), [LabGroup.Spaces]: _td("Spaces"), @@ -129,13 +129,13 @@ export interface IBaseSetting { // Display names are strongly recommended for clarity. // Display name can also be an object for different levels. displayName?: - | string + | TranslationKey | Partial<{ - [level in SettingLevel]: string; + [level in SettingLevel]: TranslationKey; }>; // Optional description which will be shown as microCopy under SettingsFlags - description?: string | (() => ReactNode); + description?: TranslationKey | (() => ReactNode); // The supported levels are required. Preferably, use the preset arrays // at the top of this file to define this rather than a custom array. @@ -165,11 +165,11 @@ export interface IBaseSetting { // XXX: Keep this around for re-use in future Betas betaInfo?: { - title: string; // _td + title: TranslationKey; caption: () => ReactNode; faq?: (enabled: boolean) => ReactNode; image?: string; // require(...) - feedbackSubheading?: string; + feedbackSubheading?: TranslationKey; feedbackLabel?: string; extraSettings?: string[]; requiresRefresh?: boolean; @@ -266,7 +266,7 @@ export const SETTINGS: { [setting: string]: ISetting } = { labsGroup: LabGroup.Moderation, displayName: _td("Report to moderators"), description: _td( - "In rooms that support moderation, " + "the “Report” button will let you report abuse to room moderators.", + "In rooms that support moderation, the “Report” button will let you report abuse to room moderators.", ), supportedLevels: LEVELS_FEATURE, default: false, @@ -805,7 +805,7 @@ export const SETTINGS: { [setting: string]: ISetting } = { "deviceClientInformationOptIn": { supportedLevels: [SettingLevel.ACCOUNT], displayName: _td( - `Record the client name, version, and url ` + `to recognise sessions more easily in session manager`, + "Record the client name, version, and url to recognise sessions more easily in session manager", ), default: false, }, diff --git a/src/settings/SettingsStore.ts b/src/settings/SettingsStore.ts index 0637776802..48ffbd656a 100644 --- a/src/settings/SettingsStore.ts +++ b/src/settings/SettingsStore.ts @@ -269,7 +269,7 @@ export default class SettingsStore { else displayName = displayName["default"]; } - return _t(displayName as string); + return displayName ? _t(displayName) : null; } /** diff --git a/src/slash-commands/command.ts b/src/slash-commands/command.ts index 30fa7732f9..9f230c609e 100644 --- a/src/slash-commands/command.ts +++ b/src/slash-commands/command.ts @@ -22,7 +22,7 @@ import { SlashCommand as SlashCommandEvent } from "@matrix-org/analytics-events/ import { TimelineRenderingType } from "../contexts/RoomContext"; import { reject } from "./utils"; -import { _t, UserFriendlyError } from "../languageHandler"; +import { _t, TranslationKey, UserFriendlyError } from "../languageHandler"; import { PosthogAnalytics } from "../PosthogAnalytics"; import { CommandCategories, RunResult } from "./interface"; @@ -38,7 +38,7 @@ interface ICommandOpts { command: string; aliases?: string[]; args?: string; - description: string; + description: TranslationKey; analyticsName?: SlashCommandEvent["command"]; runFn?: RunFn; category: string; @@ -51,7 +51,7 @@ export class Command { public readonly command: string; public readonly aliases: string[]; public readonly args?: string; - public readonly description: string; + public readonly description: TranslationKey; public readonly runFn?: RunFn; public readonly category: string; public readonly hideCompletionAfterSpace: boolean; diff --git a/src/stores/RoomViewStore.tsx b/src/stores/RoomViewStore.tsx index 53d4f0781a..50c060c446 100644 --- a/src/stores/RoomViewStore.tsx +++ b/src/stores/RoomViewStore.tsx @@ -630,9 +630,7 @@ export class RoomViewStore extends EventEmitter { description = (

    {_t( - "You attempted to join using a room ID without providing a list " + - "of servers to join through. Room IDs are internal identifiers and " + - "cannot be used to join a room without additional information.", + "You attempted to join using a room ID without providing a list of servers to join through. Room IDs are internal identifiers and cannot be used to join a room without additional information.", )}

    diff --git a/src/toasts/AnalyticsToast.tsx b/src/toasts/AnalyticsToast.tsx index 0a5b00cd58..8011594d43 100644 --- a/src/toasts/AnalyticsToast.tsx +++ b/src/toasts/AnalyticsToast.tsx @@ -104,8 +104,7 @@ export const showToast = (): void => { ); props = { description: _t( - "Share anonymous data to help us identify issues. Nothing personal. No third parties. " + - "Learn More", + "Share anonymous data to help us identify issues. Nothing personal. No third parties. Learn More", {}, { LearnMoreLink: learnMoreLink }, ), diff --git a/src/toasts/MobileGuideToast.ts b/src/toasts/MobileGuideToast.ts index 26d2cf12f6..2c7df129b4 100644 --- a/src/toasts/MobileGuideToast.ts +++ b/src/toasts/MobileGuideToast.ts @@ -45,8 +45,7 @@ export const showToast = (): void => { title: _t("Use app for a better experience"), props: { description: _t( - "%(brand)s is experimental on a mobile web browser. " + - "For a better experience and the latest features, use our free native app.", + "%(brand)s is experimental on a mobile web browser. For a better experience and the latest features, use our free native app.", { brand }, ), acceptLabel: _t("Use app"), diff --git a/src/utils/AutoDiscoveryUtils.tsx b/src/utils/AutoDiscoveryUtils.tsx index 8027eb7292..55e62455c2 100644 --- a/src/utils/AutoDiscoveryUtils.tsx +++ b/src/utils/AutoDiscoveryUtils.tsx @@ -24,7 +24,7 @@ import { } from "matrix-js-sdk/src/matrix"; import { logger } from "matrix-js-sdk/src/logger"; -import { _t, UserFriendlyError } from "../languageHandler"; +import { _t, TranslationKey, UserFriendlyError } from "../languageHandler"; import SdkConfig from "../SdkConfig"; import { ValidatedServerConfig } from "./ValidatedServerConfig"; @@ -104,21 +104,15 @@ export default class AutoDiscoveryUtils { // don't make this easy to avoid. if (pageName === "register") { body = _t( - "You can register, but some features will be unavailable until the identity server is " + - "back online. If you keep seeing this warning, check your configuration or contact a server " + - "admin.", + "You can register, but some features will be unavailable until the identity server is back online. If you keep seeing this warning, check your configuration or contact a server admin.", ); } else if (pageName === "reset_password") { body = _t( - "You can reset your password, but some features will be unavailable until the identity " + - "server is back online. If you keep seeing this warning, check your configuration or contact " + - "a server admin.", + "You can reset your password, but some features will be unavailable until the identity server is back online. If you keep seeing this warning, check your configuration or contact a server admin.", ); } else { body = _t( - "You can log in, but some features will be unavailable until the identity server is " + - "back online. If you keep seeing this warning, check your configuration or contact a server " + - "admin.", + "You can log in, but some features will be unavailable until the identity server is back online. If you keep seeing this warning, check your configuration or contact a server admin.", ); } } @@ -223,7 +217,8 @@ export default class AutoDiscoveryUtils { logger.error("Error determining preferred identity server URL:", isResult); if (isResult.state === AutoDiscovery.FAIL_ERROR) { if (AutoDiscovery.ALL_ERRORS.indexOf(isResult.error as string) !== -1) { - throw new UserFriendlyError(String(isResult.error)); + // XXX: We mark these with _td at the top of Login.tsx - we should come up with a better solution + throw new UserFriendlyError(String(isResult.error) as TranslationKey); } throw new UserFriendlyError("Unexpected error resolving identity server configuration"); } // else the error is not related to syntax - continue anyways. @@ -239,7 +234,8 @@ export default class AutoDiscoveryUtils { logger.error("Error processing homeserver config:", hsResult); if (!syntaxOnly || !AutoDiscoveryUtils.isLivelinessError(hsResult.error)) { if (AutoDiscovery.ALL_ERRORS.indexOf(hsResult.error as string) !== -1) { - throw new UserFriendlyError(String(hsResult.error)); + // XXX: We mark these with _td at the top of Login.tsx - we should come up with a better solution + throw new UserFriendlyError(String(hsResult.error) as TranslationKey); } if (hsResult.error === AutoDiscovery.ERROR_HOMESERVER_TOO_OLD) { throw new UserFriendlyError( diff --git a/src/utils/ErrorUtils.tsx b/src/utils/ErrorUtils.tsx index 2fe8e1da31..91fe19bd17 100644 --- a/src/utils/ErrorUtils.tsx +++ b/src/utils/ErrorUtils.tsx @@ -17,7 +17,7 @@ limitations under the License. import React, { ReactNode } from "react"; import { MatrixError, ConnectionError } from "matrix-js-sdk/src/matrix"; -import { _t, _td, Tags, TranslatedString } from "../languageHandler"; +import { _t, _td, Tags, TranslatedString, TranslationKey } from "../languageHandler"; import SdkConfig from "../SdkConfig"; import { ValidatedServerConfig } from "./ValidatedServerConfig"; import ExternalLink from "../components/views/elements/ExternalLink"; @@ -49,7 +49,7 @@ export const adminContactStrings = { export function messageForResourceLimitError( limitType: string | undefined, adminContact: string | undefined, - strings: Record, + strings: Record, extraTranslations?: Tags, ): TranslatedString { let errString = limitType ? strings[limitType] : undefined; @@ -154,8 +154,7 @@ export function messageForConnectionError( return ( {_t( - "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. " + - "Either use HTTPS or enable unsafe scripts.", + "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or enable unsafe scripts.", {}, { a: (sub) => { @@ -178,9 +177,7 @@ export function messageForConnectionError( return ( {_t( - "Can't connect to homeserver - please check your connectivity, ensure your " + - "homeserver's SSL certificate is trusted, and that a browser extension " + - "is not blocking requests.", + "Can't connect to homeserver - please check your connectivity, ensure your homeserver's SSL certificate is trusted, and that a browser extension is not blocking requests.", {}, { a: (sub) => ( diff --git a/src/utils/MultiInviter.ts b/src/utils/MultiInviter.ts index edbe6c03b4..05e2b06283 100644 --- a/src/utils/MultiInviter.ts +++ b/src/utils/MultiInviter.ts @@ -308,8 +308,7 @@ export default class MultiInviter { case "ORG.MATRIX.JSSDK_MISSING_PARAM": if (getAddressType(address) === AddressType.Email) { errorText = _t( - "Cannot invite user by email without an identity server. " + - 'You can connect to one under "Settings".', + 'Cannot invite user by email without an identity server. You can connect to one under "Settings".', ); } } diff --git a/src/utils/PasswordScorer.ts b/src/utils/PasswordScorer.ts index 093c49b067..7acbcf1110 100644 --- a/src/utils/PasswordScorer.ts +++ b/src/utils/PasswordScorer.ts @@ -17,7 +17,7 @@ limitations under the License. import zxcvbn, { ZXCVBNFeedbackWarning } from "zxcvbn"; import { MatrixClient } from "matrix-js-sdk/src/matrix"; -import { _t, _td } from "../languageHandler"; +import { _t, _td, TranslationKey } from "../languageHandler"; import { MatrixClientPeg } from "../MatrixClientPeg"; const ZXCVBN_USER_INPUTS = ["riot", "matrix"]; @@ -90,8 +90,9 @@ export function scorePassword( } for (let i = 0; i < zxcvbnResult.feedback.suggestions.length; ++i) { - // translate suggestions - zxcvbnResult.feedback.suggestions[i] = _t(zxcvbnResult.feedback.suggestions[i]); + // translate suggestions - we ensure we mark them as `_td` at the top of this file + // https://github.com/dropbox/zxcvbn/issues/284 will be a better approach when it lands + zxcvbnResult.feedback.suggestions[i] = _t(zxcvbnResult.feedback.suggestions[i] as TranslationKey); } // and warning, if any if (zxcvbnResult.feedback.warning) { diff --git a/src/utils/leave-behaviour.ts b/src/utils/leave-behaviour.ts index b751baa950..775d54cc56 100644 --- a/src/utils/leave-behaviour.ts +++ b/src/utils/leave-behaviour.ts @@ -135,8 +135,7 @@ export async function leaveRoomBehaviour( Modal.createDialog(ErrorDialog, { title: _t("Can't leave Server Notices room"), description: _t( - "This room is used for important messages from the Homeserver, " + - "so you cannot leave it.", + "This room is used for important messages from the Homeserver, so you cannot leave it.", ), }); return; diff --git a/src/utils/location/LocationShareErrors.ts b/src/utils/location/LocationShareErrors.ts index a59c929592..2e0905fa36 100644 --- a/src/utils/location/LocationShareErrors.ts +++ b/src/utils/location/LocationShareErrors.ts @@ -32,8 +32,7 @@ export const getLocationShareErrorMessage = (errorType?: LocationShareError): st case LocationShareError.MapStyleUrlNotReachable: default: return _t( - `This homeserver is not configured correctly to display maps, ` + - `or the configured map server may be unreachable.`, + "This homeserver is not configured correctly to display maps, or the configured map server may be unreachable.", ); } }; diff --git a/src/utils/location/positionFailureMessage.ts b/src/utils/location/positionFailureMessage.ts index a5c1e6e60b..72a1223479 100644 --- a/src/utils/location/positionFailureMessage.ts +++ b/src/utils/location/positionFailureMessage.ts @@ -27,8 +27,7 @@ export const positionFailureMessage = (code: number): string | undefined => { switch (code) { case 1: return _t( - "%(brand)s was denied permission to fetch your location. " + - "Please allow location access in your browser settings.", + "%(brand)s was denied permission to fetch your location. Please allow location access in your browser settings.", { brand }, ); case 2: diff --git a/src/voice-broadcast/components/molecules/ConfirmListenBroadcastStopCurrent.tsx b/src/voice-broadcast/components/molecules/ConfirmListenBroadcastStopCurrent.tsx index 0affa3beba..427c137b93 100644 --- a/src/voice-broadcast/components/molecules/ConfirmListenBroadcastStopCurrent.tsx +++ b/src/voice-broadcast/components/molecules/ConfirmListenBroadcastStopCurrent.tsx @@ -30,8 +30,7 @@ export const ConfirmListenBroadcastStopCurrentDialog: React.FC = ({ onFin

    {_t( - "If you start listening to this live broadcast, " + - "your current live broadcast recording will be ended.", + "If you start listening to this live broadcast, your current live broadcast recording will be ended.", )}

    => { description: (

    {_t( - "Are you sure you want to stop your live broadcast? " + - "This will end the broadcast and the full recording will be available in the room.", + "Are you sure you want to stop your live broadcast? This will end the broadcast and the full recording will be available in the room.", )}

    ), diff --git a/src/voice-broadcast/utils/checkVoiceBroadcastPreConditions.tsx b/src/voice-broadcast/utils/checkVoiceBroadcastPreConditions.tsx index c5dfd416b4..8d186ff550 100644 --- a/src/voice-broadcast/utils/checkVoiceBroadcastPreConditions.tsx +++ b/src/voice-broadcast/utils/checkVoiceBroadcastPreConditions.tsx @@ -28,8 +28,7 @@ const showAlreadyRecordingDialog = (): void => { description: (

    {_t( - "You are already recording a voice broadcast. " + - "Please end your current voice broadcast to start a new one.", + "You are already recording a voice broadcast. Please end your current voice broadcast to start a new one.", )}

    ), @@ -43,8 +42,7 @@ const showInsufficientPermissionsDialog = (): void => { description: (

    {_t( - "You don't have the required permissions to start a voice broadcast in this room. " + - "Contact a room administrator to upgrade your permissions.", + "You don't have the required permissions to start a voice broadcast in this room. Contact a room administrator to upgrade your permissions.", )}

    ), @@ -58,8 +56,7 @@ const showOthersAlreadyRecordingDialog = (): void => { description: (

    {_t( - "Someone else is already recording a voice broadcast. " + - "Wait for their voice broadcast to end to start a new one.", + "Someone else is already recording a voice broadcast. Wait for their voice broadcast to end to start a new one.", )}

    ), diff --git a/src/voice-broadcast/utils/showCantStartACallDialog.tsx b/src/voice-broadcast/utils/showCantStartACallDialog.tsx index 61124ea96e..ec7d11c77e 100644 --- a/src/voice-broadcast/utils/showCantStartACallDialog.tsx +++ b/src/voice-broadcast/utils/showCantStartACallDialog.tsx @@ -26,8 +26,7 @@ export const showCantStartACallDialog = (): void => { description: (

    {_t( - "You can’t start a call as you are currently recording a live broadcast. " + - "Please end your live broadcast in order to start a call.", + "You can’t start a call as you are currently recording a live broadcast. Please end your live broadcast in order to start a call.", )}

    ), diff --git a/src/widgets/CapabilityText.tsx b/src/widgets/CapabilityText.tsx index 1d302f6c47..9d34833249 100644 --- a/src/widgets/CapabilityText.tsx +++ b/src/widgets/CapabilityText.tsx @@ -29,7 +29,7 @@ import { import { EventType, MsgType } from "matrix-js-sdk/src/matrix"; import React from "react"; -import { _t, _td, TranslatedString } from "../languageHandler"; +import { _t, _td, TranslatedString, TranslationKey } from "../languageHandler"; import { ElementWidgetCapabilities } from "../stores/widgets/ElementWidgetCapabilities"; import { MatrixClientPeg } from "../MatrixClientPeg"; import TextWithTooltip from "../components/views/elements/TextWithTooltip"; @@ -38,7 +38,10 @@ type GENERIC_WIDGET_KIND = "generic"; // eslint-disable-line @typescript-eslint/ const GENERIC_WIDGET_KIND: GENERIC_WIDGET_KIND = "generic"; type SendRecvStaticCapText = Partial< - Record>>> + Record< + EventType | string, + Partial>> + > >; export interface TranslatedCapabilityText { @@ -47,7 +50,7 @@ export interface TranslatedCapabilityText { } export class CapabilityText { - private static simpleCaps: Record>> = { + private static simpleCaps: Record>> = { [MatrixCapabilities.AlwaysOnScreen]: { [WidgetKind.Room]: _td("Remain on your screen when viewing another room, when running"), [GENERIC_WIDGET_KIND]: _td("Remain on your screen while running"), diff --git a/test/TextForEvent-test.ts b/test/TextForEvent-test.ts index f86b780869..4a8258879c 100644 --- a/test/TextForEvent-test.ts +++ b/test/TextForEvent-test.ts @@ -14,7 +14,15 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { EventType, JoinRule, MatrixClient, MatrixEvent, Room, RoomMember } from "matrix-js-sdk/src/matrix"; +import { + EventType, + HistoryVisibility, + JoinRule, + MatrixClient, + MatrixEvent, + Room, + RoomMember, +} from "matrix-js-sdk/src/matrix"; import { render } from "@testing-library/react"; import { ReactElement } from "react"; import { Mocked, mocked } from "jest-mock"; @@ -571,4 +579,39 @@ describe("TextForEvent", () => { ).toEqual("@a changed the join rule to a not implemented one"); }); }); + + describe("textForHistoryVisibilityEvent()", () => { + type TestCase = [string, { result: string }]; + const testCases: TestCase[] = [ + [ + HistoryVisibility.Invited, + { result: "@a made future room history visible to all room members, from the point they are invited." }, + ], + [ + HistoryVisibility.Joined, + { result: "@a made future room history visible to all room members, from the point they joined." }, + ], + [HistoryVisibility.Shared, { result: "@a made future room history visible to all room members." }], + [HistoryVisibility.WorldReadable, { result: "@a made future room history visible to anyone." }], + ]; + + it.each(testCases)( + "returns correct message when room join rule changed to %s", + (historyVisibility, { result }) => { + expect( + textForEvent( + new MatrixEvent({ + type: "m.room.history_visibility", + sender: "@a", + content: { + history_visibility: historyVisibility, + }, + state_key: "", + }), + mockClient, + ), + ).toEqual(result); + }, + ); + }); }); diff --git a/test/components/views/beta/BetaCard-test.tsx b/test/components/views/beta/BetaCard-test.tsx index bd8f8fa735..d2c457ccf8 100644 --- a/test/components/views/beta/BetaCard-test.tsx +++ b/test/components/views/beta/BetaCard-test.tsx @@ -21,6 +21,7 @@ import { render, screen } from "@testing-library/react"; import { shouldShowFeedback } from "../../../../src/utils/Feedback"; import BetaCard from "../../../../src/components/views/beta/BetaCard"; import SettingsStore from "../../../../src/settings/SettingsStore"; +import { TranslationKey } from "../../../../src/languageHandler"; jest.mock("../../../../src/utils/Feedback"); jest.mock("../../../../src/settings/SettingsStore"); @@ -31,10 +32,10 @@ describe("", () => { beforeEach(() => { mocked(SettingsStore).getBetaInfo.mockReturnValue({ - title: "title", + title: "title" as TranslationKey, caption: () => "caption", feedbackLabel: "feedbackLabel", - feedbackSubheading: "feedbackSubheading", + feedbackSubheading: "feedbackSubheading" as TranslationKey, }); mocked(SettingsStore).getValue.mockReturnValue(true); mocked(shouldShowFeedback).mockReturnValue(true); @@ -53,9 +54,9 @@ describe("", () => { it("should not show feedback prompt if label is unset", () => { mocked(SettingsStore).getBetaInfo.mockReturnValue({ - title: "title", + title: "title" as TranslationKey, caption: () => "caption", - feedbackSubheading: "feedbackSubheading", + feedbackSubheading: "feedbackSubheading" as TranslationKey, }); render(); expect(screen.queryByText("Feedback")).toBeFalsy(); @@ -63,7 +64,7 @@ describe("", () => { it("should not show feedback prompt if subheading is unset", () => { mocked(SettingsStore).getBetaInfo.mockReturnValue({ - title: "title", + title: "title" as TranslationKey, caption: () => "caption", feedbackLabel: "feedbackLabel", }); diff --git a/test/components/views/settings/discovery/EmailAddresses-test.tsx b/test/components/views/settings/discovery/EmailAddresses-test.tsx index 3728acf565..b2eaa3bd06 100644 --- a/test/components/views/settings/discovery/EmailAddresses-test.tsx +++ b/test/components/views/settings/discovery/EmailAddresses-test.tsx @@ -19,7 +19,7 @@ import { fireEvent, render, screen } from "@testing-library/react"; import { IThreepid, ThreepidMedium } from "matrix-js-sdk/src/@types/threepids"; import { IRequestTokenResponse, MatrixError } from "matrix-js-sdk/src/matrix"; -import { UserFriendlyError } from "../../../../../src/languageHandler"; +import { TranslationKey, UserFriendlyError } from "../../../../../src/languageHandler"; import EmailAddresses, { EmailAddress } from "../../../../../src/components/views/settings/discovery/EmailAddresses"; import { clearAllModals, getMockClientWithEventEmitter } from "../../../../test-utils"; @@ -119,7 +119,7 @@ describe("", () => { }); it("Shows error dialog when share completion fails (UserFriendlyError)", async () => { - const fakeErrorText = "Fake UserFriendlyError error in test"; + const fakeErrorText = "Fake UserFriendlyError error in test" as TranslationKey; mockClient.bindThreePid.mockRejectedValue(new UserFriendlyError(fakeErrorText)); fireEvent.click(screen.getByText("Complete")); diff --git a/test/languageHandler-test.tsx b/test/languageHandler-test.tsx index d27de458c4..dc9b353676 100644 --- a/test/languageHandler-test.tsx +++ b/test/languageHandler-test.tsx @@ -30,6 +30,7 @@ import { substitute, TranslatedString, UserFriendlyError, + TranslationKey, } from "../src/languageHandler"; import { stubClient } from "./test-utils"; import { setupLanguageMock } from "./setup/setupLanguage"; @@ -61,9 +62,9 @@ describe("languageHandler", () => { }); it("should support overriding translations", async () => { - const str = "This is a test string that does not exist in the app."; - const enOverride = "This is the English version of a custom string."; - const deOverride = "This is the German version of a custom string."; + const str = "This is a test string that does not exist in the app." as TranslationKey; + const enOverride = "This is the English version of a custom string." as TranslationKey; + const deOverride = "This is the German version of a custom string." as TranslationKey; // First test that overrides aren't being used await setLanguage("en"); @@ -87,7 +88,7 @@ describe("languageHandler", () => { }); describe("UserFriendlyError", () => { - const testErrorMessage = "This email address is already in use (%(email)s)"; + const testErrorMessage = "This email address is already in use (%(email)s)" as TranslationKey; beforeEach(async () => { // Setup some strings with variable substituations that we can use in the tests. const deOverride = "Diese E-Mail-Adresse wird bereits verwendet (%(email)s)"; @@ -128,7 +129,7 @@ describe("languageHandler", () => { }); it("ok to omit the substitution variables and cause object, there just won't be any cause", async () => { - const friendlyError = new UserFriendlyError("foo error"); + const friendlyError = new UserFriendlyError("foo error" as TranslationKey); expect(friendlyError.cause).toBeUndefined(); }); }); @@ -171,12 +172,18 @@ describe("languageHandler", () => { describe("languageHandler JSX", function () { // See setupLanguage.ts for how we are stubbing out translations to provide fixture data for these tests const basicString = "Rooms"; - const selfClosingTagSub = "Accept to continue:"; - const textInTagSub = "Upgrade to your own domain"; + const selfClosingTagSub = "Accept to continue:" as TranslationKey; + const textInTagSub = "Upgrade to your own domain" as TranslationKey; const plurals = "and %(count)s others..."; const variableSub = "You are now ignoring %(userId)s"; - type TestCase = [string, string, Record, Record | undefined, TranslatedString]; + type TestCase = [ + string, + TranslationKey, + Record, + Record | undefined, + TranslatedString, + ]; const testCasesEn: TestCase[] = [ // description of the test case, translationString, variables, tags, expected result ["translates a basic string", basicString, {}, undefined, "Rooms"], @@ -253,7 +260,7 @@ describe("languageHandler JSX", function () { }); it("replacements in the wrong order", function () { - const text = "%(var1)s %(var2)s"; + const text = "%(var1)s %(var2)s" as TranslationKey; expect(_t(text, { var2: "val2", var1: "val1" })).toBe("val1 val2"); }); @@ -354,12 +361,12 @@ describe("languageHandler JSX", function () { describe("when languages dont load", () => { it("_t", () => { - const STRING_NOT_IN_THE_DICTIONARY = "a string that isn't in the translations dictionary"; + const STRING_NOT_IN_THE_DICTIONARY = "a string that isn't in the translations dictionary" as TranslationKey; expect(_t(STRING_NOT_IN_THE_DICTIONARY, {})).toEqual(STRING_NOT_IN_THE_DICTIONARY); }); it("_tDom", () => { - const STRING_NOT_IN_THE_DICTIONARY = "a string that isn't in the translations dictionary"; + const STRING_NOT_IN_THE_DICTIONARY = "a string that isn't in the translations dictionary" as TranslationKey; expect(_tDom(STRING_NOT_IN_THE_DICTIONARY, {})).toEqual( {STRING_NOT_IN_THE_DICTIONARY}, ); diff --git a/test/settings/controllers/ServerSupportUnstableFeatureController-test.ts b/test/settings/controllers/ServerSupportUnstableFeatureController-test.ts index 8fe676d2b0..806187a109 100644 --- a/test/settings/controllers/ServerSupportUnstableFeatureController-test.ts +++ b/test/settings/controllers/ServerSupportUnstableFeatureController-test.ts @@ -23,6 +23,7 @@ import { LabGroup, SETTINGS } from "../../../src/settings/Settings"; import { stubClient } from "../../test-utils"; import { WatchManager } from "../../../src/settings/WatchManager"; import MatrixClientBackedController from "../../../src/settings/controllers/MatrixClientBackedController"; +import { TranslationKey } from "../../../src/languageHandler"; describe("ServerSupportUnstableFeatureController", () => { const watchers = new WatchManager(); @@ -35,7 +36,7 @@ describe("ServerSupportUnstableFeatureController", () => { SETTINGS[setting] = { isFeature: true, labsGroup: LabGroup.Messaging, - displayName: "name of some kind", + displayName: "name of some kind" as TranslationKey, supportedLevels: [SettingLevel.DEVICE, SettingLevel.CONFIG], default: false, controller, diff --git a/test/utils/AutoDiscoveryUtils-test.tsx b/test/utils/AutoDiscoveryUtils-test.tsx index 81692aad91..e5e1ec086a 100644 --- a/test/utils/AutoDiscoveryUtils-test.tsx +++ b/test/utils/AutoDiscoveryUtils-test.tsx @@ -87,6 +87,20 @@ describe("AutoDiscoveryUtils", () => { expect(logger.error).toHaveBeenCalled(); }); + it("throws an error when homeserver config has fail error and recognised error string", () => { + const discoveryResult = { + ...validIsConfig, + "m.homeserver": { + state: AutoDiscoveryAction.FAIL_ERROR, + error: AutoDiscovery.ERROR_INVALID_HOMESERVER, + }, + }; + expect(() => AutoDiscoveryUtils.buildValidatedConfigFromDiscovery(serverName, discoveryResult)).toThrow( + "Unexpected error resolving homeserver configuration", + ); + expect(logger.error).toHaveBeenCalled(); + }); + it("throws an error with fallback message identity server config has fail error", () => { const discoveryResult = { ...validHsConfig, @@ -249,4 +263,12 @@ describe("AutoDiscoveryUtils", () => { ); }); }); + + describe("authComponentStateForError", () => { + const error = new Error("TEST"); + + it("should return expected error for the registration page", () => { + expect(AutoDiscoveryUtils.authComponentStateForError(error, "register")).toMatchSnapshot(); + }); + }); }); diff --git a/test/utils/__snapshots__/AutoDiscoveryUtils-test.tsx.snap b/test/utils/__snapshots__/AutoDiscoveryUtils-test.tsx.snap new file mode 100644 index 0000000000..b53de96973 --- /dev/null +++ b/test/utils/__snapshots__/AutoDiscoveryUtils-test.tsx.snap @@ -0,0 +1,26 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AutoDiscoveryUtils authComponentStateForError should return expected error for the registration page 1`] = ` +{ + "serverDeadError":
    + + Your Element is misconfigured + +
    + + Ask your Element admin to check + + your config + + for incorrect or duplicate entries. + +
    +
    , + "serverErrorIsFatal": true, + "serverIsAlive": false, +} +`;