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>
This commit is contained in:
Michael Telatynski 2023-08-22 16:32:05 +01:00 committed by GitHub
parent d13b6e1b41
commit ac70f7ac9b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
157 changed files with 554 additions and 780 deletions

View file

@ -23,22 +23,41 @@ export type Writeable<T> = { -readonly [P in keyof T]: T[P] };
export type ComponentClass = keyof JSX.IntrinsicElements | JSXElementConstructor<any>; export type ComponentClass = keyof JSX.IntrinsicElements | JSXElementConstructor<any>;
// Utility type for string dot notation for accessing nested object properties /**
// Based on https://stackoverflow.com/a/58436959 * Utility type for string dot notation for accessing nested object properties.
type Join<K, P> = K extends string | number * 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<Target, Separator extends string = ".", LeafType = string, MaxDepth extends number = 3> = [
MaxDepth,
] extends [never]
? never
: Target extends LeafType
? ""
: {
[K in keyof Target]-?: Join<K, Leaves<Target[K], Separator, LeafType, Prev[MaxDepth]>, Separator>;
}[keyof Target];
type Prev = [never, 0, 1, 2, 3, ...0[]];
type Join<K, P, S extends string = "."> = K extends string | number
? P extends string | number ? P extends string | number
? `${K}${"" extends P ? "" : "."}${P}` ? `${K}${"" extends P ? "" : S}${P}`
: never : never
: never; : never;
type Prev = [never, 0, 1, 2, 3, ...0[]];
export type Leaves<T, D extends number = 3> = [D] extends [never]
? never
: T extends object
? { [K in keyof T]-?: Join<K, Leaves<T[K], Prev[D]>> }[keyof T]
: "";
export type RecursivePartial<T> = { export type RecursivePartial<T> = {
[P in keyof T]?: T[P] extends (infer U)[] [P in keyof T]?: T[P] extends (infer U)[]
? RecursivePartial<U>[] ? RecursivePartial<U>[]

View file

@ -141,9 +141,7 @@ export default class IdentityAuthClient {
<div> <div>
<p> <p>
{_t( {_t(
"This action requires accessing the default identity server " + "This action requires accessing the default identity server <server /> to validate an email address or phone number, but the server does not have any terms of service.",
"<server /> to validate an email address or phone number, " +
"but the server does not have any terms of service.",
{}, {},
{ {
server: () => <b>{abbreviateUrl(identityServerUrl)}</b>, server: () => <b>{abbreviateUrl(identityServerUrl)}</b>,

View file

@ -823,19 +823,14 @@ export default class LegacyCallHandler extends EventEmitter {
<div> <div>
<p> <p>
{_t( {_t(
"Please ask the administrator of your homeserver " + "Please ask the administrator of your homeserver (<code>%(homeserverDomain)s</code>) to configure a TURN server in order for calls to work reliably.",
"(<code>%(homeserverDomain)s</code>) to configure a TURN server in " +
"order for calls to work reliably.",
{ homeserverDomain: cli.getDomain() }, { homeserverDomain: cli.getDomain() },
{ code: (sub: string) => <code>{sub}</code> }, { code: (sub: string) => <code>{sub}</code> },
)} )}
</p> </p>
<p> <p>
{_t( {_t(
"Alternatively, you can try to use the public server at " + "Alternatively, you can try to use the public server at <server/>, but this will not be as reliable, and it will share your IP address with that server. You can also manage this in Settings.",
"<server/>, 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, undefined,
{ server: () => <code>{new URL(FALLBACK_ICE_SERVER).pathname}</code> }, { server: () => <code>{new URL(FALLBACK_ICE_SERVER).pathname}</code> },
)} )}
@ -865,8 +860,7 @@ export default class LegacyCallHandler extends EventEmitter {
description = ( description = (
<div> <div>
{_t( {_t(
"Call failed because microphone could not be accessed. " + "Call failed because microphone could not be accessed. Check that a microphone is plugged in and set up correctly.",
"Check that a microphone is plugged in and set up correctly.",
)} )}
</div> </div>
); );

View file

@ -305,8 +305,7 @@ export function attemptTokenLogin(
logger.warn("Cannot log in with token: can't determine HS URL to use"); logger.warn("Cannot log in with token: can't determine HS URL to use");
onFailedDelegatedAuthLogin( onFailedDelegatedAuthLogin(
_t( _t(
"We asked the browser to remember which homeserver you use to let you sign in, " + "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.",
"but unfortunately your browser has forgotten it. Go to the sign in page and try again.",
), ),
); );
return Promise.resolve(false); return Promise.resolve(false);

View file

@ -299,8 +299,7 @@ class NotifierClass {
const description = const description =
result === "denied" result === "denied"
? _t( ? _t(
"%(brand)s does not have permission to send you notifications - " + "%(brand)s does not have permission to send you notifications - please check your browser settings",
"please check your browser settings",
{ brand }, { brand },
) )
: _t("%(brand)s was not given permission to send notifications - please try again", { : _t("%(brand)s was not given permission to send notifications - please try again", {

View file

@ -192,8 +192,7 @@ export const Commands = [
const unixTimestamp = Date.parse(args); const unixTimestamp = Date.parse(args);
if (!unixTimestamp) { if (!unixTimestamp) {
throw new UserFriendlyError( throw new UserFriendlyError(
"We were unable to understand the given date (%(inputDate)s). " + "We were unable to understand the given date (%(inputDate)s). Try using the format YYYY-MM-DD.",
"Try using the format YYYY-MM-DD.",
{ inputDate: args, cause: undefined }, { inputDate: args, cause: undefined },
); );
} }
@ -401,9 +400,7 @@ export const Commands = [
description: ( description: (
<p> <p>
{_t( {_t(
"Use an identity server to invite by email. " + "Use an identity server to invite by email. Click continue to use the default identity server (%(defaultIdentityServerName)s) or manage in Settings.",
"Click continue to use the default identity server " +
"(%(defaultIdentityServerName)s) or manage in Settings.",
{ {
defaultIdentityServerName: abbreviateUrl(defaultIdentityServerUrl), defaultIdentityServerName: abbreviateUrl(defaultIdentityServerUrl),
}, },
@ -717,9 +714,7 @@ export const Commands = [
if (device.getFingerprint() !== fingerprint) { if (device.getFingerprint() !== fingerprint) {
const fprint = device.getFingerprint(); const fprint = device.getFingerprint();
throw new UserFriendlyError( throw new UserFriendlyError(
"WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and session" + '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!',
' %(deviceId)s is "%(fprint)s" which does not match the provided key ' +
'"%(fingerprint)s". This could mean your communications are being intercepted!',
{ {
fprint, fprint,
userId, userId,
@ -739,8 +734,7 @@ export const Commands = [
<div> <div>
<p> <p>
{_t( {_t(
"The signing key you provided matches the signing key you received " + "The signing key you provided matches the signing key you received from %(userId)s's session %(deviceId)s. Session marked as verified.",
"from %(userId)s's session %(deviceId)s. Session marked as verified.",
{ userId, deviceId }, { userId, deviceId },
)} )}
</p> </p>

View file

@ -482,17 +482,14 @@ function textForHistoryVisibilityEvent(event: MatrixEvent): (() => string) | nul
case HistoryVisibility.Invited: case HistoryVisibility.Invited:
return () => return () =>
_t( _t(
"%(senderName)s made future room history visible to all room members, " + "%(senderName)s made future room history visible to all room members, from the point they are invited.",
"from the point they are invited.",
{ senderName }, { senderName },
); );
case HistoryVisibility.Joined: case HistoryVisibility.Joined:
return () => return () =>
_t( _t("%(senderName)s made future room history visible to all room members, from the point they joined.", {
"%(senderName)s made future room history visible to all room members, " + senderName,
"from the point they joined.", });
{ senderName },
);
case HistoryVisibility.Shared: case HistoryVisibility.Shared:
return () => _t("%(senderName)s made future room history visible to all room members.", { senderName }); return () => _t("%(senderName)s made future room history visible to all room members.", { senderName });
case HistoryVisibility.WorldReadable: case HistoryVisibility.WorldReadable:
@ -810,33 +807,31 @@ function textForMjolnirEvent(event: MatrixEvent): (() => string) | null {
if (USER_RULE_TYPES.includes(event.getType())) { if (USER_RULE_TYPES.includes(event.getType())) {
return () => return () =>
_t( _t(
"%(senderName)s changed a rule that was banning users matching %(oldGlob)s to matching " + "%(senderName)s changed a rule that was banning users matching %(oldGlob)s to matching %(newGlob)s for %(reason)s",
"%(newGlob)s for %(reason)s",
{ senderName, oldGlob: prevEntity, newGlob: entity, reason }, { senderName, oldGlob: prevEntity, newGlob: entity, reason },
); );
} else if (ROOM_RULE_TYPES.includes(event.getType())) { } else if (ROOM_RULE_TYPES.includes(event.getType())) {
return () => return () =>
_t( _t(
"%(senderName)s changed a rule that was banning rooms matching %(oldGlob)s to matching " + "%(senderName)s changed a rule that was banning rooms matching %(oldGlob)s to matching %(newGlob)s for %(reason)s",
"%(newGlob)s for %(reason)s",
{ senderName, oldGlob: prevEntity, newGlob: entity, reason }, { senderName, oldGlob: prevEntity, newGlob: entity, reason },
); );
} else if (SERVER_RULE_TYPES.includes(event.getType())) { } else if (SERVER_RULE_TYPES.includes(event.getType())) {
return () => return () =>
_t( _t(
"%(senderName)s changed a rule that was banning servers matching %(oldGlob)s to matching " + "%(senderName)s changed a rule that was banning servers matching %(oldGlob)s to matching %(newGlob)s for %(reason)s",
"%(newGlob)s for %(reason)s",
{ senderName, oldGlob: prevEntity, newGlob: entity, reason }, { senderName, oldGlob: prevEntity, newGlob: entity, reason },
); );
} }
// Unknown type. We'll say something but we shouldn't end up here. // Unknown type. We'll say something but we shouldn't end up here.
return () => return () =>
_t( _t("%(senderName)s updated a ban rule that was matching %(oldGlob)s to matching %(newGlob)s for %(reason)s", {
"%(senderName)s updated a ban rule that was matching %(oldGlob)s to matching %(newGlob)s " + senderName,
"for %(reason)s", oldGlob: prevEntity,
{ senderName, oldGlob: prevEntity, newGlob: entity, reason }, newGlob: entity,
); reason,
});
} }
export function textForLocationEvent(event: MatrixEvent): () => string { export function textForLocationEvent(event: MatrixEvent): () => string {

View file

@ -25,9 +25,9 @@ import {
IKeyboardShortcuts, IKeyboardShortcuts,
KeyBindingAction, KeyBindingAction,
KEYBOARD_SHORTCUTS, KEYBOARD_SHORTCUTS,
KeyboardShortcutSetting,
MAC_ONLY_SHORTCUTS, MAC_ONLY_SHORTCUTS,
} from "./KeyboardShortcuts"; } from "./KeyboardShortcuts";
import { IBaseSetting } from "../settings/Settings";
/** /**
* This function gets the keyboard shortcuts that should be presented in the UI * 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 => { export const getKeyboardShortcutsForUI = (): IKeyboardShortcuts => {
const entries = [...Object.entries(getUIOnlyShortcuts()), ...Object.entries(getKeyboardShortcuts())] as [ const entries = [...Object.entries(getUIOnlyShortcuts()), ...Object.entries(getKeyboardShortcuts())] as [
KeyBindingAction, KeyBindingAction,
IBaseSetting<KeyCombo>, KeyboardShortcutSetting,
][]; ][];
return entries.reduce((acc, [key, value]) => { return entries.reduce((acc, [key, value]) => {
@ -130,5 +130,5 @@ export const getKeyboardShortcutValue = (name: KeyBindingAction): KeyCombo | und
export const getKeyboardShortcutDisplayName = (name: KeyBindingAction): string | undefined => { export const getKeyboardShortcutDisplayName = (name: KeyBindingAction): string | undefined => {
const keyboardShortcutDisplayName = getKeyboardShortcutsForUI()[name]?.displayName; const keyboardShortcutDisplayName = getKeyboardShortcutsForUI()[name]?.displayName;
return keyboardShortcutDisplayName && _t(keyboardShortcutDisplayName as string); return keyboardShortcutDisplayName && _t(keyboardShortcutDisplayName);
}; };

View file

@ -16,7 +16,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import { _td } from "../languageHandler"; import { _td, TranslationKey } from "../languageHandler";
import { IS_MAC, Key } from "../Keyboard"; import { IS_MAC, Key } from "../Keyboard";
import { IBaseSetting } from "../settings/Settings"; import { IBaseSetting } from "../settings/Settings";
import { KeyCombo } from "../KeyBindingsManager"; import { KeyCombo } from "../KeyBindingsManager";
@ -154,13 +154,15 @@ export enum KeyBindingAction {
ToggleHiddenEventVisibility = "KeyBinding.toggleHiddenEventVisibility", ToggleHiddenEventVisibility = "KeyBinding.toggleHiddenEventVisibility",
} }
type KeyboardShortcutSetting = Omit<IBaseSetting<KeyCombo>, "supportedLevels">; export type KeyboardShortcutSetting = Omit<IBaseSetting<KeyCombo>, "supportedLevels" | "displayName"> & {
displayName?: TranslationKey;
};
// TODO: We should figure out what to do with the keyboard shortcuts that are not handled by KeybindingManager // TODO: We should figure out what to do with the keyboard shortcuts that are not handled by KeybindingManager
export type IKeyboardShortcuts = Partial<Record<KeyBindingAction, KeyboardShortcutSetting>>; export type IKeyboardShortcuts = Partial<Record<KeyBindingAction, KeyboardShortcutSetting>>;
export interface ICategory { 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 // TODO: We should figure out what to do with the keyboard shortcuts that are not handled by KeybindingManager
settingNames: KeyBindingAction[]; 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 // Meta-key representing the digits [0-9] often found at the top of standard keyboard layouts
export const DIGITS = "digits"; export const DIGITS = "digits";
export const ALTERNATE_KEY_NAME: Record<string, string> = { export const ALTERNATE_KEY_NAME: Record<string, TranslationKey> = {
[Key.PAGE_UP]: _td("Page Up"), [Key.PAGE_UP]: _td("Page Up"),
[Key.PAGE_DOWN]: _td("Page Down"), [Key.PAGE_DOWN]: _td("Page Down"),
[Key.ESCAPE]: _td("Esc"), [Key.ESCAPE]: _td("Esc"),

View file

@ -559,8 +559,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
<form onSubmit={this.onChooseKeyPassphraseFormSubmit}> <form onSubmit={this.onChooseKeyPassphraseFormSubmit}>
<p className="mx_CreateSecretStorageDialog_centeredBody"> <p className="mx_CreateSecretStorageDialog_centeredBody">
{_t( {_t(
"Safeguard against losing access to encrypted messages & data by " + "Safeguard against losing access to encrypted messages & data by backing up encryption keys on your server.",
"backing up encryption keys on your server.",
)} )}
</p> </p>
<div className="mx_CreateSecretStorageDialog_primaryContainer" role="radiogroup"> <div className="mx_CreateSecretStorageDialog_primaryContainer" role="radiogroup">
@ -611,9 +610,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
<form onSubmit={this.onMigrateFormSubmit}> <form onSubmit={this.onMigrateFormSubmit}>
<p> <p>
{_t( {_t(
"Upgrade this session to allow it to verify other sessions, " + "Upgrade this session to allow it to verify other sessions, granting them access to encrypted messages and marking them as trusted for other users.",
"granting them access to encrypted messages and marking them " +
"as trusted for other users.",
)} )}
</p> </p>
<div>{authPrompt}</div> <div>{authPrompt}</div>
@ -636,8 +633,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
<form onSubmit={this.onPassPhraseNextClick}> <form onSubmit={this.onPassPhraseNextClick}>
<p> <p>
{_t( {_t(
"Enter a Security Phrase only you know, as it's used to safeguard your data. " + "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.",
"To be secure, you shouldn't re-use your account password.",
)} )}
</p> </p>
@ -752,8 +748,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
<div> <div>
<p> <p>
{_t( {_t(
"Store your Security Key somewhere safe, like a password manager or a safe, " + "Store your Security Key somewhere safe, like a password manager or a safe, as it's used to safeguard your encrypted data.",
"as it's used to safeguard your encrypted data.",
)} )}
</p> </p>
<div className="mx_CreateSecretStorageDialog_primaryContainer mx_CreateSecretStorageDialog_recoveryKeyPrimarycontainer"> <div className="mx_CreateSecretStorageDialog_primaryContainer mx_CreateSecretStorageDialog_recoveryKeyPrimarycontainer">

View file

@ -164,21 +164,12 @@ export default class ExportE2eKeysDialog extends React.Component<IProps, IState>
<div className="mx_Dialog_content"> <div className="mx_Dialog_content">
<p> <p>
{_t( {_t(
"This process allows you to export the keys for 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.",
"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.",
)} )}
</p> </p>
<p> <p>
{_t( {_t(
"The exported file will allow anyone who can read it to decrypt " + "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.",
"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.",
)} )}
</p> </p>
<div className="error">{this.state.errStr}</div> <div className="error">{this.state.errStr}</div>

View file

@ -146,16 +146,12 @@ export default class ImportE2eKeysDialog extends React.Component<IProps, IState>
<div className="mx_Dialog_content"> <div className="mx_Dialog_content">
<p> <p>
{_t( {_t(
"This process allows you to import encryption keys " + "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.",
"that you had previously exported from another Matrix " +
"client. You will then be able to decrypt any " +
"messages that the other client could decrypt.",
)} )}
</p> </p>
<p> <p>
{_t( {_t(
"The export file will be protected with a passphrase. " + "The export file will be protected with a passphrase. You should enter the passphrase here, to decrypt the file.",
"You should enter the passphrase here, to decrypt the file.",
)} )}
</p> </p>
<div className="error">{this.state.errStr}</div> <div className="error">{this.state.errStr}</div>

View file

@ -62,10 +62,7 @@ export default class NewRecoveryMethodDialog extends React.PureComponent<IProps>
const hackWarning = ( const hackWarning = (
<p className="warning"> <p className="warning">
{_t( {_t(
"If you didn't set the new recovery method, an " + "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.",
"attacker may be trying to access your account. " +
"Change your account password and set a new recovery " +
"method immediately in Settings.",
)} )}
</p> </p>
); );

View file

@ -53,23 +53,17 @@ export default class RecoveryMethodRemovedDialog extends React.PureComponent<IPr
<div> <div>
<p> <p>
{_t( {_t(
"This session has detected that your Security Phrase and key " + "This session has detected that your Security Phrase and key for Secure Messages have been removed.",
"for Secure Messages have been removed.",
)} )}
</p> </p>
<p> <p>
{_t( {_t(
"If you did this accidentally, you can setup Secure Messages on " + "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.",
"this session which will re-encrypt this session's message " +
"history with a new recovery method.",
)} )}
</p> </p>
<p className="warning"> <p className="warning">
{_t( {_t(
"If you didn't remove the recovery method, an " + "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.",
"attacker may be trying to access your account. " +
"Change your account password and set a new recovery " +
"method immediately in Settings.",
)} )}
</p> </p>
<DialogButtons <DialogButtons

View file

@ -21,7 +21,7 @@ import sanitizeHtml from "sanitize-html";
import classnames from "classnames"; import classnames from "classnames";
import { logger } from "matrix-js-sdk/src/logger"; import { logger } from "matrix-js-sdk/src/logger";
import { _t } from "../../languageHandler"; import { _t, TranslationKey } from "../../languageHandler";
import dis from "../../dispatcher/dispatcher"; import dis from "../../dispatcher/dispatcher";
import { MatrixClientPeg } from "../../MatrixClientPeg"; import { MatrixClientPeg } from "../../MatrixClientPeg";
import MatrixClientContext from "../../contexts/MatrixClientContext"; import MatrixClientContext from "../../contexts/MatrixClientContext";
@ -56,7 +56,7 @@ export default class EmbeddedPage extends React.PureComponent<IProps, IState> {
}; };
} }
private translate(s: string): string { private translate(s: TranslationKey): string {
return sanitizeHtml(_t(s)); return sanitizeHtml(_t(s));
} }

View file

@ -482,12 +482,10 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
const waitText = _t("Wait!"); const waitText = _t("Wait!");
const scamText = _t( 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( const devText = _t(
"If you know what you're doing, Element is open-source, " + "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!",
"be sure to check out our GitHub (https://github.com/vector-im/element-web/) " +
"and contribute!",
); );
global.mx_rage_logger.bypassRageshake( global.mx_rage_logger.bypassRageshake(
@ -1166,8 +1164,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
<span className="warning" key="only_member_warning"> <span className="warning" key="only_member_warning">
{" " /* Whitespace, otherwise the sentences get smashed together */} {" " /* Whitespace, otherwise the sentences get smashed together */}
{_t( {_t(
"You are the only person here. " + "You are the only person here. If you leave, no one will be able to join in the future, including you.",
"If you leave, no one will be able to join in the future, including you.",
)} )}
</span>, </span>,
); );
@ -1593,8 +1590,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
<p> <p>
{" "} {" "}
{_t( {_t(
"To continue using the %(homeserverDomain)s homeserver " + "To continue using the %(homeserverDomain)s homeserver you must review and agree to our terms and conditions.",
"you must review and agree to our terms and conditions.",
{ homeserverDomain: cli.getDomain() }, { homeserverDomain: cli.getDomain() },
)} )}
</p> </p>
@ -1643,13 +1639,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
Modal.createDialog(ErrorDialog, { Modal.createDialog(ErrorDialog, {
title: _t("Old cryptography data detected"), title: _t("Old cryptography data detected"),
description: _t( description: _t(
"Data from an older version of %(brand)s has been detected. " + "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.",
"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 }, { brand: SdkConfig.get().brand },
), ),
}); });

View file

@ -207,8 +207,7 @@ export default class RoomStatusBar extends React.PureComponent<IProps, IState> {
} }
if (consentError) { if (consentError) {
title = _t( title = _t(
"You can't send any messages until you review and agree to " + "You can't send any messages until you review and agree to <consentLink>our terms and conditions</consentLink>.",
"<consentLink>our terms and conditions</consentLink>.",
{}, {},
{ {
consentLink: (sub) => ( consentLink: (sub) => (
@ -224,16 +223,13 @@ export default class RoomStatusBar extends React.PureComponent<IProps, IState> {
resourceLimitError.data.admin_contact, resourceLimitError.data.admin_contact,
{ {
"monthly_active_user": _td( "monthly_active_user": _td(
"Your message wasn't sent because this homeserver has hit its Monthly Active User Limit. " + "Your message wasn't sent because this homeserver has hit its Monthly Active User Limit. Please <a>contact your service administrator</a> to continue using the service.",
"Please <a>contact your service administrator</a> to continue using the service.",
), ),
"hs_disabled": _td( "hs_disabled": _td(
"Your message wasn't sent because this homeserver has been blocked by its administrator. " + "Your message wasn't sent because this homeserver has been blocked by its administrator. Please <a>contact your service administrator</a> to continue using the service.",
"Please <a>contact your service administrator</a> to continue using the service.",
), ),
"": _td( "": _td(
"Your message wasn't sent because this homeserver has exceeded a resource limit. " + "Your message wasn't sent because this homeserver has exceeded a resource limit. Please <a>contact your service administrator</a> to continue using the service.",
"Please <a>contact your service administrator</a> to continue using the service.",
), ),
}, },
); );

View file

@ -401,8 +401,7 @@ const SpaceAddExistingRooms: React.FC<{
<h1>{_t("What do you want to organise?")}</h1> <h1>{_t("What do you want to organise?")}</h1>
<div className="mx_SpaceRoomView_description"> <div className="mx_SpaceRoomView_description">
{_t( {_t(
"Pick rooms or conversations to add. This is just a space for you, " + "Pick rooms or conversations to add. This is just a space for you, no one will be informed. You can add more later.",
"no one will be informed. You can add more later.",
)} )}
</div> </div>

View file

@ -20,7 +20,7 @@ import * as React from "react";
import classNames from "classnames"; import classNames from "classnames";
import { logger } from "matrix-js-sdk/src/logger"; import { logger } from "matrix-js-sdk/src/logger";
import { _t } from "../../languageHandler"; import { _t, TranslationKey } from "../../languageHandler";
import AutoHideScrollbar from "./AutoHideScrollbar"; import AutoHideScrollbar from "./AutoHideScrollbar";
import { PosthogScreenTracker, ScreenName } from "../../PosthogTrackers"; import { PosthogScreenTracker, ScreenName } from "../../PosthogTrackers";
import { NonEmptyArray } from "../../@types/common"; import { NonEmptyArray } from "../../@types/common";
@ -40,7 +40,7 @@ export class Tab<T extends string> {
*/ */
public constructor( public constructor(
public readonly id: T, public readonly id: T,
public readonly label: string, public readonly label: TranslationKey,
public readonly icon: string | null, public readonly icon: string | null,
public readonly body: React.ReactNode, public readonly body: React.ReactNode,
public readonly screenName?: ScreenName, public readonly screenName?: ScreenName,

View file

@ -147,8 +147,7 @@ const EmptyThread: React.FC<EmptyThreadIProps> = ({ hasThreads, filterOption, sh
<> <>
<p> <p>
{_t( {_t(
"Reply to an ongoing thread or use “%(replyInThread)s” " + "Reply to an ongoing thread or use “%(replyInThread)s” when hovering over a message to start a new one.",
"when hovering over a message to start a new one.",
{ {
replyInThread: _t("Reply in thread"), replyInThread: _t("Reply in thread"),
}, },

View file

@ -1651,8 +1651,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
let description: string; let description: string;
if (error.errcode == "M_FORBIDDEN") { if (error.errcode == "M_FORBIDDEN") {
description = _t( description = _t(
"Tried to load a specific point in this room's timeline, but you " + "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.",
"do not have permission to view the message in question.",
); );
} else { } else {
description = _t("Tried to load a specific point in this room's timeline, but was unable to find it."); description = _t("Tried to load a specific point in this room's timeline, but was unable to find it.");

View file

@ -75,8 +75,7 @@ export const WaitingForThirdPartyRoomView: React.FC<Props> = ({ roomView, resize
className="mx_cryptoEvent mx_cryptoEvent_icon" className="mx_cryptoEvent mx_cryptoEvent_icon"
title={_t("Waiting for users to join %(brand)s", { brand })} title={_t("Waiting for users to join %(brand)s", { brand })}
subtitle={_t( subtitle={_t(
"Once invited users have joined %(brand)s, " + "Once invited users have joined %(brand)s, you will be able to chat and the room will be end-to-end encrypted",
"you will be able to chat and the room will be end-to-end encrypted",
{ brand }, { brand },
)} )}
/> />

View file

@ -354,14 +354,12 @@ export default class ForgotPassword extends React.Component<Props, State> {
<div> <div>
<p> <p>
{_t( {_t(
"Signing out your devices will delete the message encryption keys stored on them, " + "Signing out your devices will delete the message encryption keys stored on them, making encrypted chat history unreadable.",
"making encrypted chat history unreadable.",
)} )}
</p> </p>
<p> <p>
{_t( {_t(
"If you want to retain access to your chat history in encrypted rooms, set up Key Backup " + "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.",
"or export your message keys from one of your other devices before proceeding.",
)} )}
</p> </p>
</div> </div>
@ -443,9 +441,7 @@ export default class ForgotPassword extends React.Component<Props, State> {
{this.state.logoutDevices ? ( {this.state.logoutDevices ? (
<p> <p>
{_t( {_t(
"You have been logged out of all devices and will no longer receive " + "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.",
"push notifications. To re-enable notifications, sign in again on each " +
"device.",
)} )}
</p> </p>
) : null} ) : null}

View file

@ -622,8 +622,7 @@ export default class Registration extends React.Component<IProps, IState> {
<div> <div>
<p> <p>
{_t( {_t(
"Your new account (%(newAccountId)s) is registered, but you're already " + "Your new account (%(newAccountId)s) is registered, but you're already logged into a different account (%(loggedInUserId)s).",
"logged into a different account (%(loggedInUserId)s).",
{ {
newAccountId: this.state.registeredUsername, newAccountId: this.state.registeredUsername,
loggedInUserId: this.state.differentLoggedInUserId, loggedInUserId: this.state.differentLoggedInUserId,

View file

@ -161,10 +161,7 @@ export default class SetupEncryptionBody extends React.Component<IProps, IState>
<div> <div>
<p> <p>
{_t( {_t(
"It looks like you don't have a Security Key or any other devices you can " + "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.",
"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.",
)} )}
</p> </p>
@ -234,8 +231,7 @@ export default class SetupEncryptionBody extends React.Component<IProps, IState>
message = ( message = (
<p> <p>
{_t( {_t(
"Your new device is now verified. It has access to your " + "Your new device is now verified. It has access to your encrypted messages, and other users will see it as trusted.",
"encrypted messages, and other users will see it as trusted.",
)} )}
</p> </p>
); );
@ -258,8 +254,7 @@ export default class SetupEncryptionBody extends React.Component<IProps, IState>
<div> <div>
<p> <p>
{_t( {_t(
"Without verifying, you won't have access to all your messages " + "Without verifying, you won't have access to all your messages and may appear as untrusted to others.",
"and may appear as untrusted to others.",
)} )}
</p> </p>
<div className="mx_CompleteSecurity_actionRow"> <div className="mx_CompleteSecurity_actionRow">
@ -277,16 +272,12 @@ export default class SetupEncryptionBody extends React.Component<IProps, IState>
<div> <div>
<p> <p>
{_t( {_t(
"Resetting your verification keys cannot be undone. After resetting, " + "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.",
"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.",
)} )}
</p> </p>
<p> <p>
{_t( {_t(
"Please only proceed if you're sure you've lost all of your other " + "Please only proceed if you're sure you've lost all of your other devices and your Security Key.",
"devices and your Security Key.",
)} )}
</p> </p>

View file

@ -281,8 +281,7 @@ export default class SoftLogout extends React.Component<IProps, IState> {
let introText: string | null = null; // null is translated to something area specific in this function let introText: string | null = null; // null is translated to something area specific in this function
if (this.state.keyBackupNeeded) { if (this.state.keyBackupNeeded) {
introText = _t( introText = _t(
"Regain access to your account and recover encryption keys stored in this 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.",
"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<IProps, IState> {
// Default: assume unsupported/error // Default: assume unsupported/error
return ( return (
<p> <p>
{_t( {_t("You cannot sign in to your account. Please contact your homeserver admin for more information.")}
"You cannot sign in to your account. Please contact your " +
"homeserver admin for more information.",
)}
</p> </p>
); );
} }
@ -350,9 +346,7 @@ export default class SoftLogout extends React.Component<IProps, IState> {
<h2>{_t("Clear personal data")}</h2> <h2>{_t("Clear personal data")}</h2>
<p> <p>
{_t( {_t(
"Warning: your personal data (including encryption keys) is still stored " + "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.",
"in this session. Clear it if you're finished using this session, or want to sign " +
"in to another account.",
)} )}
</p> </p>
<div> <div>

View file

@ -54,8 +54,7 @@ export const VerifyEmailModal: React.FC<Props> = ({
<h1>{_t("Verify your email to continue")}</h1> <h1>{_t("Verify your email to continue")}</h1>
<p> <p>
{_t( {_t(
"We need to know its you before resetting your password. " + "We need to know its you before resetting your password. Click the link in the email we just sent to <b>%(email)s</b>",
"Click the link in the email we just sent to <b>%(email)s</b>",
{ {
email, email,
}, },

View file

@ -17,7 +17,7 @@ limitations under the License.
import React, { PureComponent, RefCallback, RefObject } from "react"; import React, { PureComponent, RefCallback, RefObject } from "react";
import Field, { IInputProps } from "../elements/Field"; 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 withValidation, { IFieldState, IValidationResult } from "../elements/Validation";
import * as Email from "../../../email"; import * as Email from "../../../email";
@ -27,9 +27,9 @@ interface IProps extends Omit<IInputProps, "onValidate" | "element"> {
value: string; value: string;
autoFocus?: boolean; autoFocus?: boolean;
label?: string; label: TranslationKey;
labelRequired?: string; labelRequired: TranslationKey;
labelInvalid?: string; labelInvalid: TranslationKey;
// When present, completely overrides the default validation rules. // When present, completely overrides the default validation rules.
validationRules?: (fieldState: IFieldState) => Promise<IValidationResult>; validationRules?: (fieldState: IFieldState) => Promise<IValidationResult>;
@ -50,12 +50,12 @@ class EmailField extends PureComponent<IProps> {
{ {
key: "required", key: "required",
test: ({ value, allowEmpty }) => allowEmpty || !!value, test: ({ value, allowEmpty }) => allowEmpty || !!value,
invalid: () => _t(this.props.labelRequired!), invalid: () => _t(this.props.labelRequired),
}, },
{ {
key: "email", key: "email",
test: ({ value }) => !value || Email.looksValid(value), test: ({ value }) => !value || Email.looksValid(value),
invalid: () => _t(this.props.labelInvalid!), invalid: () => _t(this.props.labelInvalid),
}, },
], ],
}); });
@ -80,7 +80,7 @@ class EmailField extends PureComponent<IProps> {
id={this.props.id} id={this.props.id}
ref={this.props.fieldRef} ref={this.props.fieldRef}
type="text" type="text"
label={_t(this.props.label!)} label={_t(this.props.label)}
value={this.props.value} value={this.props.value}
autoFocus={this.props.autoFocus} autoFocus={this.props.autoFocus}
onChange={this.props.onChange} onChange={this.props.onChange}

View file

@ -220,8 +220,7 @@ export class RecaptchaAuthEntry extends React.Component<IRecaptchaAuthEntryProps
let sitePublicKey: string | undefined; let sitePublicKey: string | undefined;
if (!this.props.stageParams || !this.props.stageParams.public_key) { if (!this.props.stageParams || !this.props.stageParams.public_key) {
errorText = _t( errorText = _t(
"Missing captcha public key in homeserver configuration. Please report " + "Missing captcha public key in homeserver configuration. Please report this to your homeserver administrator.",
"this to your homeserver administrator.",
); );
} else { } else {
sitePublicKey = this.props.stageParams.public_key; sitePublicKey = this.props.stageParams.public_key;

View file

@ -18,7 +18,7 @@ import React, { PureComponent, RefCallback, RefObject } from "react";
import Field, { IInputProps } from "../elements/Field"; import Field, { IInputProps } from "../elements/Field";
import withValidation, { IFieldState, IValidationResult } from "../elements/Validation"; import withValidation, { IFieldState, IValidationResult } from "../elements/Validation";
import { _t, _td } from "../../../languageHandler"; import { _t, _td, TranslationKey } from "../../../languageHandler";
interface IProps extends Omit<IInputProps, "onValidate" | "label" | "element"> { interface IProps extends Omit<IInputProps, "onValidate" | "label" | "element"> {
id?: string; id?: string;
@ -27,9 +27,9 @@ interface IProps extends Omit<IInputProps, "onValidate" | "label" | "element"> {
value: string; value: string;
password: string; // The password we're confirming password: string; // The password we're confirming
label: string; label: TranslationKey;
labelRequired: string; labelRequired: TranslationKey;
labelInvalid: string; labelInvalid: TranslationKey;
onChange(ev: React.FormEvent<HTMLElement>): void; onChange(ev: React.FormEvent<HTMLElement>): void;
onValidate?(result: IValidationResult): void; onValidate?(result: IValidationResult): void;

View file

@ -20,7 +20,7 @@ import zxcvbn from "zxcvbn";
import SdkConfig from "../../../SdkConfig"; import SdkConfig from "../../../SdkConfig";
import withValidation, { IFieldState, IValidationResult } from "../elements/Validation"; 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 Field, { IInputProps } from "../elements/Field";
import { MatrixClientPeg } from "../../../MatrixClientPeg"; import { MatrixClientPeg } from "../../../MatrixClientPeg";
@ -34,10 +34,10 @@ interface IProps extends Omit<IInputProps, "onValidate" | "element"> {
// Additional strings such as a username used to catch bad passwords // Additional strings such as a username used to catch bad passwords
userInputs?: string[]; userInputs?: string[];
label: string; label: TranslationKey;
labelEnterPassword: string; labelEnterPassword: TranslationKey;
labelStrongPassword: string; labelStrongPassword: TranslationKey;
labelAllowedButUnsafe: string; labelAllowedButUnsafe: TranslationKey;
onChange(ev: React.FormEvent<HTMLElement>): void; onChange(ev: React.FormEvent<HTMLElement>): void;
onValidate?(result: IValidationResult): void; onValidate?(result: IValidationResult): void;

View file

@ -19,9 +19,9 @@ import React, { useEffect, useState } from "react";
import MediaDeviceHandler, { MediaDeviceKindEnum } from "../../../MediaDeviceHandler"; import MediaDeviceHandler, { MediaDeviceKindEnum } from "../../../MediaDeviceHandler";
import IconizedContextMenu, { IconizedContextMenuOptionList, IconizedContextMenuRadio } from "./IconizedContextMenu"; import IconizedContextMenu, { IconizedContextMenuOptionList, IconizedContextMenuRadio } from "./IconizedContextMenu";
import { IProps as IContextMenuProps } from "../../structures/ContextMenu"; import { IProps as IContextMenuProps } from "../../structures/ContextMenu";
import { _t, _td } from "../../../languageHandler"; import { _t, _td, TranslationKey } from "../../../languageHandler";
const SECTION_NAMES: Record<MediaDeviceKindEnum, string> = { const SECTION_NAMES: Record<MediaDeviceKindEnum, TranslationKey> = {
[MediaDeviceKindEnum.AudioInput]: _td("Input devices"), [MediaDeviceKindEnum.AudioInput]: _td("Input devices"),
[MediaDeviceKindEnum.AudioOutput]: _td("Output devices"), [MediaDeviceKindEnum.AudioOutput]: _td("Output devices"),
[MediaDeviceKindEnum.VideoInput]: _td("Cameras"), [MediaDeviceKindEnum.VideoInput]: _td("Cameras"),

View file

@ -192,8 +192,7 @@ export const WidgetContextMenu: React.FC<IProps> = ({
Modal.createDialog(QuestionDialog, { Modal.createDialog(QuestionDialog, {
title: _t("Delete Widget"), title: _t("Delete Widget"),
description: _t( description: _t(
"Deleting a widget removes it for all users in this room." + "Deleting a widget removes it for all users in this room. Are you sure you want to delete this widget?",
" Are you sure you want to delete this widget?",
), ),
button: _t("Delete widget"), button: _t("Delete widget"),
onFinished: (confirmed) => { onFinished: (confirmed) => {

View file

@ -20,7 +20,7 @@ import { Room, EventType } from "matrix-js-sdk/src/matrix";
import { sleep } from "matrix-js-sdk/src/utils"; import { sleep } from "matrix-js-sdk/src/utils";
import { logger } from "matrix-js-sdk/src/logger"; import { logger } from "matrix-js-sdk/src/logger";
import { _t, _td } from "../../../languageHandler"; import { _t, _td, TranslationKey } from "../../../languageHandler";
import BaseDialog from "./BaseDialog"; import BaseDialog from "./BaseDialog";
import Dropdown from "../elements/Dropdown"; import Dropdown from "../elements/Dropdown";
import SearchBox from "../../structures/SearchBox"; import SearchBox from "../../structures/SearchBox";
@ -357,7 +357,7 @@ export const AddExistingToSpace: React.FC<IAddExistingToSpaceProps> = ({
}; };
const defaultRendererFactory = const defaultRendererFactory =
(title: string): Renderer => (title: TranslationKey): Renderer =>
(rooms, selectedToAdd, { scrollTop, height }, onChange) => (rooms, selectedToAdd, { scrollTop, height }, onChange) =>
( (
<div className="mx_AddExistingToSpace_section"> <div className="mx_AddExistingToSpace_section">

View file

@ -78,9 +78,7 @@ export const AnalyticsLearnMoreDialog: React.FC<IProps> = ({
<div className="mx_AnalyticsLearnMore_image_holder" /> <div className="mx_AnalyticsLearnMore_image_holder" />
<div className="mx_AnalyticsLearnMore_copy"> <div className="mx_AnalyticsLearnMore_copy">
{_t( {_t(
"Help us identify issues and improve %(analyticsOwner)s by sharing anonymous usage data. " + "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.",
"To understand how people use multiple devices, we'll generate a random identifier, " +
"shared by your devices.",
{ analyticsOwner }, { analyticsOwner },
)} )}
</div> </div>

View file

@ -224,10 +224,7 @@ export default class BugReportDialog extends React.Component<IProps, IState> {
{warning} {warning}
<p> <p>
{_t( {_t(
"Debug logs contain application usage data including your " + "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.",
"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.",
)} )}
</p> </p>
<p> <p>
@ -273,10 +270,7 @@ export default class BugReportDialog extends React.Component<IProps, IState> {
onChange={this.onTextChange} onChange={this.onTextChange}
value={this.state.text} value={this.state.text}
placeholder={_t( placeholder={_t(
"If there is additional context that would help in " + "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.",
"analysing the issue, such as what you were doing at " +
"the time, room IDs, user IDs, etc., " +
"please include those things here.",
)} )}
/> />
{progress} {progress}

View file

@ -114,16 +114,13 @@ const BulkRedactDialog: React.FC<Props> = (props) => {
<div className="mx_Dialog_content" id="mx_Dialog_content"> <div className="mx_Dialog_content" id="mx_Dialog_content">
<p> <p>
{_t( {_t(
"You are about to remove %(count)s messages by %(user)s. " + "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?",
"This will remove them permanently for everyone in the conversation. " +
"Do you wish to continue?",
{ count, user }, { count, user },
)} )}
</p> </p>
<p> <p>
{_t( {_t(
"For a large amount of messages, this might take some time. " + "For a large amount of messages, this might take some time. Please don't refresh your client in the meantime.",
"Please don't refresh your client in the meantime.",
)} )}
</p> </p>
<StyledCheckbox checked={keepStateEvents} onChange={(e) => setKeepStateEvents(e.target.checked)}> <StyledCheckbox checked={keepStateEvents} onChange={(e) => setKeepStateEvents(e.target.checked)}>
@ -131,8 +128,7 @@ const BulkRedactDialog: React.FC<Props> = (props) => {
</StyledCheckbox> </StyledCheckbox>
<div className="mx_BulkRedactDialog_checkboxMicrocopy"> <div className="mx_BulkRedactDialog_checkboxMicrocopy">
{_t( {_t(
"Uncheck if you also want to remove system messages on this user " + "Uncheck if you also want to remove system messages on this user (e.g. membership change, profile change…)",
"(e.g. membership change, profile change…)",
)} )}
</div> </div>
</div> </div>

View file

@ -26,8 +26,7 @@ export const createCantStartVoiceMessageBroadcastDialog = (): void => {
description: ( description: (
<p> <p>
{_t( {_t(
"You can't start a voice message as you are currently recording a live broadcast. " + "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.",
"Please end your live broadcast in order to start recording a voice message.",
)} )}
</p> </p>
), ),

View file

@ -44,8 +44,7 @@ export default class ConfirmWipeDeviceDialog extends React.Component<IProps> {
<div className="mx_ConfirmWipeDeviceDialog_content"> <div className="mx_ConfirmWipeDeviceDialog_content">
<p> <p>
{_t( {_t(
"Clearing all data from this session is permanent. Encrypted messages will be lost " + "Clearing all data from this session is permanent. Encrypted messages will be lost unless their keys have been backed up.",
"unless their keys have been backed up.",
)} )}
</p> </p>
</div> </div>

View file

@ -311,8 +311,7 @@ export default class CreateRoomDialog extends React.Component<IProps, IState> {
} }
} else { } else {
microcopy = _t( microcopy = _t(
"Your server admin has disabled end-to-end encryption by default " + "Your server admin has disabled end-to-end encryption by default in private rooms & Direct Messages.",
"in private rooms & Direct Messages.",
); );
} }
e2eeSection = ( e2eeSection = (
@ -330,15 +329,13 @@ export default class CreateRoomDialog extends React.Component<IProps, IState> {
} }
let federateLabel = _t( let federateLabel = _t(
"You might enable this if the room will only be used for collaborating with internal " + "You might enable this if the room will only be used for collaborating with internal teams on your homeserver. This cannot be changed later.",
"teams on your homeserver. This cannot be changed later.",
); );
if (SdkConfig.get().default_federate === false) { if (SdkConfig.get().default_federate === false) {
// We only change the label if the default setting is different to avoid jarring text changes to the // 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. // user. They will have read the implications of turning this off/on, so no need to rephrase for them.
federateLabel = _t( federateLabel = _t(
"You might disable this if the room will be used for collaborating with external " + "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.",
"teams who have their own homeserver. This cannot be changed later.",
); );
} }

View file

@ -36,9 +36,7 @@ const CryptoStoreTooNewDialog: React.FC<IProps> = (props: IProps) => {
Modal.createDialog(QuestionDialog, { Modal.createDialog(QuestionDialog, {
title: _t("Sign out"), title: _t("Sign out"),
description: _t( description: _t(
"To avoid losing your chat history, you must export your room keys " + "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",
"before logging out. You will need to go back to the newer version of " +
"%(brand)s to do this",
{ brand }, { brand },
), ),
button: _t("Sign out"), button: _t("Sign out"),
@ -53,9 +51,7 @@ const CryptoStoreTooNewDialog: React.FC<IProps> = (props: IProps) => {
}; };
const description = _t( const description = _t(
"You've previously used a newer version of %(brand)s with this session. " + "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.",
"To use this version again with end to end encryption, you will " +
"need to sign out and back in again.",
{ brand }, { brand },
); );

View file

@ -17,7 +17,7 @@ limitations under the License.
import React, { useState } from "react"; import React, { useState } from "react";
import { _t, _td } from "../../../languageHandler"; import { _t, _td, TranslationKey } from "../../../languageHandler";
import MatrixClientContext from "../../../contexts/MatrixClientContext"; import MatrixClientContext from "../../../contexts/MatrixClientContext";
import BaseDialog from "./BaseDialog"; import BaseDialog from "./BaseDialog";
import { TimelineEventEditor } from "./devtools/Event"; import { TimelineEventEditor } from "./devtools/Event";
@ -40,13 +40,13 @@ enum Category {
Other, Other,
} }
const categoryLabels: Record<Category, string> = { const categoryLabels: Record<Category, TranslationKey> = {
[Category.Room]: _td("Room"), [Category.Room]: _td("Room"),
[Category.Other]: _td("Other"), [Category.Other]: _td("Other"),
}; };
export type Tool = React.FC<IDevtoolsProps> | ((props: IDevtoolsProps) => JSX.Element); export type Tool = React.FC<IDevtoolsProps> | ((props: IDevtoolsProps) => JSX.Element);
const Tools: Record<Category, [label: string, tool: Tool][]> = { const Tools: Record<Category, [label: TranslationKey, tool: Tool][]> = {
[Category.Room]: [ [Category.Room]: [
[_td("Send custom timeline event"), TimelineEventEditor], [_td("Send custom timeline event"), TimelineEventEditor],
[_td("Explore room state"), RoomStateExplorer], [_td("Explore room state"), RoomStateExplorer],

View file

@ -70,9 +70,7 @@ export default class EndPollDialog extends React.Component<IProps> {
<QuestionDialog <QuestionDialog
title={_t("End Poll")} title={_t("End Poll")}
description={_t( description={_t(
"Are you sure you want to end this poll? " + "Are you sure you want to end this poll? This will show the final results of the poll and stop people from being able to vote.",
"This will show the final results of the poll and " +
"stop people from being able to vote.",
)} )}
button={_t("End Poll")} button={_t("End Poll")}
onFinished={(endPoll: boolean) => this.onFinished(endPoll)} onFinished={(endPoll: boolean) => this.onFinished(endPoll)}

View file

@ -96,8 +96,7 @@ const FeedbackDialog: React.FC<IProps> = (props: IProps) => {
bugReports = ( bugReports = (
<p className="mx_FeedbackDialog_section_microcopy"> <p className="mx_FeedbackDialog_section_microcopy">
{_t( {_t(
"PRO TIP: If you start a bug, please submit <debugLogsLink>debug logs</debugLogsLink> " + "PRO TIP: If you start a bug, please submit <debugLogsLink>debug logs</debugLogsLink> to help us track down the problem.",
"to help us track down the problem.",
{}, {},
{ {
debugLogsLink: (sub) => ( debugLogsLink: (sub) => (
@ -125,8 +124,7 @@ const FeedbackDialog: React.FC<IProps> = (props: IProps) => {
<h3>{_t("Report a bug")}</h3> <h3>{_t("Report a bug")}</h3>
<p> <p>
{_t( {_t(
"Please view <existingIssuesLink>existing bugs on Github</existingIssuesLink> first. " + "Please view <existingIssuesLink>existing bugs on Github</existingIssuesLink> first. No match? <newIssueLink>Start a new one</newIssueLink>.",
"No match? <newIssueLink>Start a new one</newIssueLink>.",
{}, {},
{ {
existingIssuesLink: (sub) => { existingIssuesLink: (sub) => {

View file

@ -181,17 +181,14 @@ export default class IncomingSasDialog extends React.Component<IProps, IState> {
const userDetailText = [ const userDetailText = [
<p key="p1"> <p key="p1">
{_t( {_t(
"Verify this user to mark them as trusted. " + "Verify this user to mark them as trusted. Trusting users gives you extra peace of mind when using end-to-end encrypted messages.",
"Trusting users gives you extra peace of mind when using " +
"end-to-end encrypted messages.",
)} )}
</p>, </p>,
<p key="p2"> <p key="p2">
{_t( {_t(
// NB. Below wording adjusted to singular 'session' until we have // NB. Below wording adjusted to singular 'session' until we have
// cross-signing // cross-signing
"Verifying this user will mark their session as trusted, and " + "Verifying this user will mark their session as trusted, and also mark your session as trusted to them.",
"also mark your session as trusted to them.",
)} )}
</p>, </p>,
]; ];
@ -199,15 +196,12 @@ export default class IncomingSasDialog extends React.Component<IProps, IState> {
const selfDetailText = [ const selfDetailText = [
<p key="p1"> <p key="p1">
{_t( {_t(
"Verify this device to mark it as trusted. " + "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.",
"Trusting this device gives you and other users extra peace of mind when using " +
"end-to-end encrypted messages.",
)} )}
</p>, </p>,
<p key="p2"> <p key="p2">
{_t( {_t(
"Verifying this device will mark it as trusted, and users who have verified with " + "Verifying this device will mark it as trusted, and users who have verified with you will trust this device.",
"you will trust this device.",
)} )}
</p>, </p>,
]; ];

View file

@ -43,8 +43,7 @@ export default class IntegrationsImpossibleDialog extends React.Component<IProps
<div className="mx_IntegrationsImpossibleDialog_content"> <div className="mx_IntegrationsImpossibleDialog_content">
<p> <p>
{_t( {_t(
"Your %(brand)s doesn't allow you to use an integration manager to do this. " + "Your %(brand)s doesn't allow you to use an integration manager to do this. Please contact an admin.",
"Please contact an admin.",
{ brand }, { brand },
)} )}
</p> </p>

View file

@ -1133,9 +1133,7 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
return ( return (
<div className="mx_InviteDialog_identityServer"> <div className="mx_InviteDialog_identityServer">
{_t( {_t(
"Use an identity server to invite by email. " + "Use an identity server to invite by email. <default>Use the default (%(defaultIdentityServerName)s)</default> or manage in <settings>Settings</settings>.",
"<default>Use the default (%(defaultIdentityServerName)s)</default> " +
"or manage in <settings>Settings</settings>.",
{ {
defaultIdentityServerName: abbreviateUrl(defaultIdentityServerUrl), defaultIdentityServerName: abbreviateUrl(defaultIdentityServerUrl),
}, },
@ -1158,7 +1156,7 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
return ( return (
<div className="mx_InviteDialog_identityServer"> <div className="mx_InviteDialog_identityServer">
{_t( {_t(
"Use an identity server to invite by email. " + "Manage in <settings>Settings</settings>.", "Use an identity server to invite by email. Manage in <settings>Settings</settings>.",
{}, {},
{ {
settings: (sub) => ( settings: (sub) => (
@ -1349,23 +1347,21 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
if (isSpace) { if (isSpace) {
if (identityServersEnabled) { if (identityServersEnabled) {
helpTextUntranslated = _td( helpTextUntranslated = _td(
"Invite someone using their name, email address, username " + "Invite someone using their name, email address, username (like <userId/>) or <a>share this space</a>.",
"(like <userId/>) or <a>share this space</a>.",
); );
} else { } else {
helpTextUntranslated = _td( helpTextUntranslated = _td(
"Invite someone using their name, username " + "(like <userId/>) or <a>share this space</a>.", "Invite someone using their name, username (like <userId/>) or <a>share this space</a>.",
); );
} }
} else { } else {
if (identityServersEnabled) { if (identityServersEnabled) {
helpTextUntranslated = _td( helpTextUntranslated = _td(
"Invite someone using their name, email address, username " + "Invite someone using their name, email address, username (like <userId/>) or <a>share this room</a>.",
"(like <userId/>) or <a>share this room</a>.",
); );
} else { } else {
helpTextUntranslated = _td( helpTextUntranslated = _td(
"Invite someone using their name, username " + "(like <userId/>) or <a>share this room</a>.", "Invite someone using their name, username (like <userId/>) or <a>share this room</a>.",
); );
} }
} }

View file

@ -29,19 +29,14 @@ interface IProps {
const LazyLoadingDisabledDialog: React.FC<IProps> = (props) => { const LazyLoadingDisabledDialog: React.FC<IProps> = (props) => {
const brand = SdkConfig.get().brand; const brand = SdkConfig.get().brand;
const description1 = _t( const description1 = _t(
"You've previously used %(brand)s on %(host)s with lazy loading of members enabled. " + "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.",
"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, brand,
host: props.host, host: props.host,
}, },
); );
const description2 = _t( const description2 = _t(
"If the other version of %(brand)s is still open in another tab, " + "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.",
"please close it as using %(brand)s on the same host with both " +
"lazy loading enabled and disabled simultaneously will cause issues.",
{ {
brand, brand,
}, },

View file

@ -28,9 +28,7 @@ interface IProps {
const LazyLoadingResyncDialog: React.FC<IProps> = (props) => { const LazyLoadingResyncDialog: React.FC<IProps> = (props) => {
const brand = SdkConfig.get().brand; const brand = SdkConfig.get().brand;
const description = _t( const description = _t(
"%(brand)s now uses 3-5x less memory, by only loading information " + "%(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!",
"about other users when needed. Please wait whilst we resynchronise " +
"with the server!",
{ brand }, { brand },
); );

View file

@ -63,15 +63,12 @@ const LeaveSpaceDialog: React.FC<IProps> = ({ space, onFinished }) => {
let onlyAdminWarning; let onlyAdminWarning;
if (isOnlyAdmin(space)) { if (isOnlyAdmin(space)) {
onlyAdminWarning = _t( onlyAdminWarning = _t("You're the only admin of this space. Leaving it will mean no one has control over it.");
"You're the only admin of this space. " + "Leaving it will mean no one has control over it.",
);
} else { } else {
const numChildrenOnlyAdminIn = roomsToLeave.filter(isOnlyAdmin).length; const numChildrenOnlyAdminIn = roomsToLeave.filter(isOnlyAdmin).length;
if (numChildrenOnlyAdminIn > 0) { if (numChildrenOnlyAdminIn > 0) {
onlyAdminWarning = _t( onlyAdminWarning = _t(
"You're the only admin of some of the rooms or spaces you wish to leave. " + "You're the only admin of some of the rooms or spaces you wish to leave. Leaving them will leave them without any admins.",
"Leaving them will leave them without any admins.",
); );
} }
} }

View file

@ -138,16 +138,12 @@ export default class LogoutDialog extends React.Component<IProps, IState> {
<div> <div>
<p> <p>
{_t( {_t(
"Encrypted messages are secured with end-to-end encryption. " + "Encrypted messages are secured with end-to-end encryption. Only you and the recipient(s) have the keys to read these messages.",
"Only you and the recipient(s) have the keys to read these messages.",
)} )}
</p> </p>
<p> <p>
{_t( {_t(
"When you sign out, these keys will be deleted from this device, " + "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.",
"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.",
)} )}
</p> </p>
<p>{_t("Back up your keys before signing out to avoid losing them.")}</p> <p>{_t("Back up your keys before signing out to avoid losing them.")}</p>

View file

@ -152,8 +152,7 @@ const ManageRestrictedJoinRuleDialog: React.FC<IProps> = ({ room, selected = [],
> >
<p> <p>
{_t( {_t(
"Decide which spaces can access this room. " + "Decide which spaces can access this room. If a space is selected, its members can find and join <RoomName/>.",
"If a space is selected, its members can find and join <RoomName/>.",
{}, {},
{ {
RoomName: () => <b>{room.name}</b>, RoomName: () => <b>{room.name}</b>,

View file

@ -59,8 +59,7 @@ const RegistrationEmailPromptDialog: React.FC<IProps> = ({ onFinished }) => {
<div className="mx_Dialog_content" id="mx_RegistrationEmailPromptDialog"> <div className="mx_Dialog_content" id="mx_RegistrationEmailPromptDialog">
<p> <p>
{_t( {_t(
"Just a heads up, if you don't add an email and forget your password, you could " + "Just a heads up, if you don't add an email and forget your password, you could <b>permanently lose access to your account</b>.",
"<b>permanently lose access to your account</b>.",
{}, {},
{ {
b: (sub) => <b>{sub}</b>, b: (sub) => <b>{sub}</b>,

View file

@ -317,54 +317,39 @@ export default class ReportEventDialog extends React.Component<IProps, IState> {
let subtitle: string; let subtitle: string;
switch (this.state.nature) { switch (this.state.nature) {
case Nature.Disagreement: case Nature.Disagreement:
subtitle = _t( subtitle = _t("What this user is writing is wrong.\nThis will be reported to the room moderators.");
"What this user is writing is wrong.\n" + "This will be reported to the room moderators.",
);
break; break;
case Nature.Toxic: case Nature.Toxic:
subtitle = _t( subtitle = _t(
"This user is displaying toxic behaviour, " + "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.",
"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.",
); );
break; break;
case Nature.Illegal: case Nature.Illegal:
subtitle = _t( subtitle = _t(
"This user is displaying illegal behaviour, " + "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.",
"for instance by doxing people or threatening violence.\n" +
"This will be reported to the room moderators who may escalate this to legal authorities.",
); );
break; break;
case Nature.Spam: case Nature.Spam:
subtitle = _t( subtitle = _t(
"This user is spamming the room with ads, links to ads or to propaganda.\n" + "This user is spamming the room with ads, links to ads or to propaganda.\nThis will be reported to the room moderators.",
"This will be reported to the room moderators.",
); );
break; break;
case NonStandardValue.Admin: case NonStandardValue.Admin:
if (client.isRoomEncrypted(this.props.mxEvent.getRoomId()!)) { if (client.isRoomEncrypted(this.props.mxEvent.getRoomId()!)) {
subtitle = _t( subtitle = _t(
"This room is dedicated to illegal or toxic content " + "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.",
"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.",
{ homeserver: homeServerName }, { homeserver: homeServerName },
); );
} else { } else {
subtitle = _t( subtitle = _t(
"This room is dedicated to illegal or toxic content " + "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.",
"or the moderators fail to moderate illegal or toxic content.\n" +
"This will be reported to the administrators of %(homeserver)s.",
{ homeserver: homeServerName }, { homeserver: homeServerName },
); );
} }
break; break;
case Nature.Other: case Nature.Other:
subtitle = _t( subtitle = _t(
"Any other reason. Please describe the problem.\n" + "Any other reason. Please describe the problem.\nThis will be reported to the room moderators.",
"This will be reported to the room moderators.",
); );
break; break;
default: default:
@ -464,10 +449,7 @@ export default class ReportEventDialog extends React.Component<IProps, IState> {
<div className="mx_ReportEventDialog" id="mx_ReportEventDialog"> <div className="mx_ReportEventDialog" id="mx_ReportEventDialog">
<p> <p>
{_t( {_t(
"Reporting this message will send its unique 'event ID' to the administrator of " + "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.",
"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.",
)} )}
</p> </p>
{adminMessage} {adminMessage}

View file

@ -94,9 +94,7 @@ export default class RoomUpgradeDialog extends React.Component<IProps, IState> {
> >
<p> <p>
{_t( {_t(
"Upgrading this room requires closing down the current " + "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:",
"instance of the room and creating a new room in its place. " +
"To give room members the best possible experience, we will:",
)} )}
</p> </p>
<ol> <ol>
@ -104,14 +102,12 @@ export default class RoomUpgradeDialog extends React.Component<IProps, IState> {
<li>{_t("Update any local room aliases to point to the new room")}</li> <li>{_t("Update any local room aliases to point to the new room")}</li>
<li> <li>
{_t( {_t(
"Stop users from speaking in the old version of the room, " + "Stop users from speaking in the old version of the room, and post a message advising users to move to the new room",
"and post a message advising users to move to the new room",
)} )}
</li> </li>
<li> <li>
{_t( {_t(
"Put a link back to the old room at the start of the new room " + "Put a link back to the old room at the start of the new room so people can see old messages",
"so people can see old messages",
)} )}
</li> </li>
</ol> </ol>

View file

@ -135,8 +135,7 @@ export default class RoomUpgradeWarningDialog extends React.Component<IProps, IS
let bugReports = ( let bugReports = (
<p> <p>
{_t( {_t(
"This usually only affects how the room is processed on the server. If you're " + "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.",
"having problems with your %(brand)s, please report a bug.",
{ brand }, { brand },
)} )}
</p> </p>
@ -145,8 +144,7 @@ export default class RoomUpgradeWarningDialog extends React.Component<IProps, IS
bugReports = ( bugReports = (
<p> <p>
{_t( {_t(
"This usually only affects how the room is processed on the server. If you're " + "This usually only affects how the room is processed on the server. If you're having problems with your %(brand)s, please <a>report a bug</a>.",
"having problems with your %(brand)s, please <a>report a bug</a>.",
{ {
brand, brand,
}, },
@ -195,14 +193,12 @@ export default class RoomUpgradeWarningDialog extends React.Component<IProps, IS
<p> <p>
{this.props.description || {this.props.description ||
_t( _t(
"Upgrading a room is an advanced action and is usually recommended when a room " + "Upgrading a room is an advanced action and is usually recommended when a room is unstable due to bugs, missing features or security vulnerabilities.",
"is unstable due to bugs, missing features or security vulnerabilities.",
)} )}
</p> </p>
<p> <p>
{_t( {_t(
"<b>Please note upgrading will make a new version of the room</b>. " + "<b>Please note upgrading will make a new version of the room</b>. All current messages will stay in this archived room.",
"All current messages will stay in this archived room.",
{}, {},
{ {
b: (sub) => <b>{sub}</b>, b: (sub) => <b>{sub}</b>,

View file

@ -107,8 +107,7 @@ export default class ServerOfflineDialog extends React.PureComponent<IProps> {
<div className="mx_ServerOfflineDialog_content"> <div className="mx_ServerOfflineDialog_content">
<p> <p>
{_t( {_t(
"Your server isn't responding to some of your requests. " + "Your server isn't responding to some of your requests. Below are some of the most likely reasons.",
"Below are some of the most likely reasons.",
)} )}
</p> </p>
<ul> <ul>

View file

@ -37,9 +37,7 @@ export default class SeshatResetDialog extends React.PureComponent<Props> {
{_t("You most likely do not want to reset your event index store")} {_t("You most likely do not want to reset your event index store")}
<br /> <br />
{_t( {_t(
"If you do, please note that none of your messages will be deleted, " + "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",
"but the search experience might be degraded for a few moments " +
"whilst the index is recreated",
)} )}
</p> </p>
</div> </div>

View file

@ -101,17 +101,14 @@ export default class SessionRestoreErrorDialog extends React.Component<IProps> {
<p> <p>
{_t( {_t(
"If you have previously used a more recent version of %(brand)s, your session " + "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.",
"may be incompatible with this version. Close this window and return " +
"to the more recent version.",
{ brand }, { brand },
)} )}
</p> </p>
<p> <p>
{_t( {_t(
"Clearing your browser's storage may fix the problem, but will sign you " + "Clearing your browser's storage may fix the problem, but will sign you out and cause any encrypted chat history to become unreadable.",
"out and cause any encrypted chat history to become unreadable.",
)} )}
</p> </p>
</div> </div>

View file

@ -78,8 +78,7 @@ export default class SetEmailDialog extends React.Component<IProps, IState> {
Modal.createDialog(QuestionDialog, { Modal.createDialog(QuestionDialog, {
title: _t("Verification Pending"), title: _t("Verification Pending"),
description: _t( description: _t(
"Please check your email and click on the link it contains. Once this " + "Please check your email and click on the link it contains. Once this is done, click continue.",
"is done, click continue.",
), ),
button: _t("Continue"), button: _t("Continue"),
onFinished: this.onEmailDialogFinished, onFinished: this.onEmailDialogFinished,

View file

@ -59,8 +59,7 @@ const SpacePreferencesAppearanceTab: React.FC<Pick<IProps, "space">> = ({ space
</StyledCheckbox> </StyledCheckbox>
<SettingsSubsectionText> <SettingsSubsectionText>
{_t( {_t(
"This groups your chats with members of this space. " + "This groups your chats with members of this space. Turning this off will hide those chats from your view of %(spaceName)s.",
"Turning this off will hide those chats from your view of %(spaceName)s.",
{ {
spaceName: space.name, spaceName: space.name,
}, },

View file

@ -65,9 +65,7 @@ export default class StorageEvictedDialog extends React.Component<IProps> {
<div className="mx_Dialog_content" id="mx_Dialog_content"> <div className="mx_Dialog_content" id="mx_Dialog_content">
<p> <p>
{_t( {_t(
"Some session data, including encrypted message keys, is " + "Some session data, including encrypted message keys, is missing. Sign out and sign in to fix this, restoring keys from backup.",
"missing. Sign out and sign in to fix this, restoring keys " +
"from backup.",
)} )}
</p> </p>
<p> <p>

View file

@ -17,7 +17,7 @@ limitations under the License.
import React, { ChangeEvent, createRef } from "react"; import React, { ChangeEvent, createRef } from "react";
import Field from "../elements/Field"; import Field from "../elements/Field";
import { _t, _td } from "../../../languageHandler"; import { _t, _td, TranslationKey } from "../../../languageHandler";
import { IFieldState, IValidationResult } from "../elements/Validation"; import { IFieldState, IValidationResult } from "../elements/Validation";
import BaseDialog from "./BaseDialog"; import BaseDialog from "./BaseDialog";
import DialogButtons from "../elements/DialogButtons"; import DialogButtons from "../elements/DialogButtons";
@ -28,7 +28,7 @@ interface IProps {
value: string; value: string;
placeholder?: string; placeholder?: string;
button?: string; button?: string;
busyMessage: string; // pass _td string busyMessage: TranslationKey;
focus: boolean; focus: boolean;
hasCancel: boolean; hasCancel: boolean;
validator?: (fieldState: IFieldState) => Promise<IValidationResult>; // result of withValidation validator?: (fieldState: IFieldState) => Promise<IValidationResult>; // result of withValidation

View file

@ -49,8 +49,7 @@ export default class UploadFailureDialog extends React.Component<IProps> {
let buttons; let buttons;
if (this.props.totalFiles === 1 && this.props.badFiles.length === 1) { if (this.props.totalFiles === 1 && this.props.badFiles.length === 1) {
message = _t( message = _t(
"This file is <b>too large</b> to upload. " + "This file is <b>too large</b> to upload. The file size limit is %(limit)s but this file is %(sizeOfThisFile)s.",
"The file size limit is %(limit)s but this file is %(sizeOfThisFile)s.",
{ {
limit: fileSize(this.props.contentMessages.getUploadLimit()), limit: fileSize(this.props.contentMessages.getUploadLimit()),
sizeOfThisFile: fileSize(this.props.badFiles[0].size), sizeOfThisFile: fileSize(this.props.badFiles[0].size),
@ -69,7 +68,7 @@ export default class UploadFailureDialog extends React.Component<IProps> {
); );
} else if (this.props.totalFiles === this.props.badFiles.length) { } else if (this.props.totalFiles === this.props.badFiles.length) {
message = _t( message = _t(
"These files are <b>too large</b> to upload. " + "The file size limit is %(limit)s.", "These files are <b>too large</b> to upload. The file size limit is %(limit)s.",
{ {
limit: fileSize(this.props.contentMessages.getUploadLimit()), limit: fileSize(this.props.contentMessages.getUploadLimit()),
}, },
@ -87,7 +86,7 @@ export default class UploadFailureDialog extends React.Component<IProps> {
); );
} else { } else {
message = _t( message = _t(
"Some files are <b>too large</b> to be uploaded. " + "The file size limit is %(limit)s.", "Some files are <b>too large</b> to be uploaded. The file size limit is %(limit)s.",
{ {
limit: fileSize(this.props.contentMessages.getUploadLimit()), limit: fileSize(this.props.contentMessages.getUploadLimit()),
}, },

View file

@ -18,7 +18,7 @@ limitations under the License.
import React, { ChangeEvent, ReactNode, useContext, useMemo, useRef, useState } from "react"; import React, { ChangeEvent, ReactNode, useContext, useMemo, useRef, useState } from "react";
import { IContent, MatrixEvent } from "matrix-js-sdk/src/matrix"; 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 Field from "../../elements/Field";
import BaseTool, { DevtoolsContext, IDevtoolsProps } from "./BaseTool"; import BaseTool, { DevtoolsContext, IDevtoolsProps } from "./BaseTool";
import MatrixClientContext from "../../../../contexts/MatrixClientContext"; import MatrixClientContext from "../../../../contexts/MatrixClientContext";
@ -37,7 +37,7 @@ interface IEventEditorProps extends Pick<IDevtoolsProps, "onBack"> {
interface IFieldDef { interface IFieldDef {
id: string; id: string;
label: string; // _td label: TranslationKey;
default?: string; default?: string;
} }

View file

@ -21,12 +21,12 @@ import { VerificationPhase as Phase, VerificationRequestEvent } from "matrix-js-
import { CryptoEvent } from "matrix-js-sdk/src/crypto"; import { CryptoEvent } from "matrix-js-sdk/src/crypto";
import { useTypedEventEmitter, useTypedEventEmitterState } from "../../../../hooks/useEventEmitter"; import { useTypedEventEmitter, useTypedEventEmitterState } from "../../../../hooks/useEventEmitter";
import { _t, _td } from "../../../../languageHandler"; import { _t, _td, TranslationKey } from "../../../../languageHandler";
import MatrixClientContext from "../../../../contexts/MatrixClientContext"; import MatrixClientContext from "../../../../contexts/MatrixClientContext";
import BaseTool, { DevtoolsContext, IDevtoolsProps } from "./BaseTool"; import BaseTool, { DevtoolsContext, IDevtoolsProps } from "./BaseTool";
import { Tool } from "../DevtoolsDialog"; import { Tool } from "../DevtoolsDialog";
const PHASE_MAP: Record<Phase, string> = { const PHASE_MAP: Record<Phase, TranslationKey> = {
[Phase.Unsent]: _td("Unsent"), [Phase.Unsent]: _td("Unsent"),
[Phase.Requested]: _td("Requested"), [Phase.Requested]: _td("Requested"),
[Phase.Ready]: _td("Ready"), [Phase.Ready]: _td("Ready"),

View file

@ -305,8 +305,7 @@ export default class AccessSecretStorageDialog extends React.PureComponent<IProp
<p>{_t("Only do this if you have no other device to complete verification with.")}</p> <p>{_t("Only do this if you have no other device to complete verification with.")}</p>
<p> <p>
{_t( {_t(
"If you reset everything, you will restart with no trusted sessions, no trusted users, and " + "If you reset everything, you will restart with no trusted sessions, no trusted users, and might not be able to see past messages.",
"might not be able to see past messages.",
)} )}
</p> </p>
<DialogButtons <DialogButtons
@ -329,8 +328,7 @@ export default class AccessSecretStorageDialog extends React.PureComponent<IProp
<div className="mx_AccessSecretStorageDialog_keyStatus"> <div className="mx_AccessSecretStorageDialog_keyStatus">
{"\uD83D\uDC4E "} {"\uD83D\uDC4E "}
{_t( {_t(
"Unable to access secret storage. " + "Unable to access secret storage. Please verify that you entered the correct Security Phrase.",
"Please verify that you entered the correct Security Phrase.",
)} )}
</div> </div>
); );

View file

@ -44,10 +44,7 @@ export default class ConfirmDestroyCrossSigningDialog extends React.Component<IP
<div className="mx_ConfirmDestroyCrossSigningDialog_content"> <div className="mx_ConfirmDestroyCrossSigningDialog_content">
<p> <p>
{_t( {_t(
"Deleting cross-signing keys is permanent. " + "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.",
"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.",
)} )}
</p> </p>
</div> </div>

View file

@ -351,8 +351,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent<IProps,
<div> <div>
<p> <p>
{_t( {_t(
"Backup could not be decrypted with this Security Key: " + "Backup could not be decrypted with this Security Key: please verify that you entered the correct Security Key.",
"please verify that you entered the correct Security Key.",
)} )}
</p> </p>
</div> </div>
@ -363,8 +362,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent<IProps,
<div> <div>
<p> <p>
{_t( {_t(
"Backup could not be decrypted with this Security Phrase: " + "Backup could not be decrypted with this Security Phrase: please verify that you entered the correct Security Phrase.",
"please verify that you entered the correct Security Phrase.",
)} )}
</p> </p>
</div> </div>
@ -418,8 +416,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent<IProps,
</p> </p>
<p> <p>
{_t( {_t(
"Access your secure message history and set up secure " + "Access your secure message history and set up secure messaging by entering your Security Phrase.",
"messaging by entering your Security Phrase.",
)} )}
</p> </p>
@ -441,9 +438,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent<IProps,
/> />
</form> </form>
{_t( {_t(
"If you've forgotten your Security Phrase you can " + "If you've forgotten your Security Phrase you can <button1>use your Security Key</button1> or <button2>set up new recovery options</button2>",
"<button1>use your Security Key</button1> or " +
"<button2>set up new recovery options</button2>",
{}, {},
{ {
button1: (s) => ( button1: (s) => (
@ -493,8 +488,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent<IProps,
</p> </p>
<p> <p>
{_t( {_t(
"Access your secure message history and set up secure " + "Access your secure message history and set up secure messaging by entering your Security Key.",
"messaging by entering your Security Key.",
)} )}
</p> </p>
@ -516,8 +510,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent<IProps,
/> />
</div> </div>
{_t( {_t(
"If you've forgotten your Security Key you can " + "If you've forgotten your Security Key you can <button>set up new recovery options</button>",
"<button>set up new recovery options</button>",
{}, {},
{ {
button: (s) => ( button: (s) => (

View file

@ -923,10 +923,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
<div className="mx_SpotlightDialog_section mx_SpotlightDialog_hiddenResults" role="group"> <div className="mx_SpotlightDialog_section mx_SpotlightDialog_hiddenResults" role="group">
<h4>{_t("Some results may be hidden")}</h4> <h4>{_t("Some results may be hidden")}</h4>
<div className="mx_SpotlightDialog_otherSearches_messageSearchText"> <div className="mx_SpotlightDialog_otherSearches_messageSearchText">
{_t( {_t("If you can't find the room you're looking for, ask for an invite or create a new room.")}
"If you can't find the room you're looking for, " +
"ask for an invite or create a new room.",
)}
</div> </div>
<Option <Option
id="mx_SpotlightDialog_button_createNewRoom" id="mx_SpotlightDialog_button_createNewRoom"

View file

@ -17,7 +17,7 @@ limitations under the License.
import React from "react"; import React from "react";
import classNames from "classnames"; import classNames from "classnames";
import { _t } from "../../../languageHandler"; import { _t, _td, TranslationKey } from "../../../languageHandler";
import BaseDialog from "..//dialogs/BaseDialog"; import BaseDialog from "..//dialogs/BaseDialog";
import DialogButtons from "./DialogButtons"; import DialogButtons from "./DialogButtons";
import AccessibleButton from "./AccessibleButton"; import AccessibleButton from "./AccessibleButton";
@ -135,7 +135,7 @@ export default class DesktopCapturerSourcePicker extends React.Component<PickerI
this.props.onFinished(); this.props.onFinished();
}; };
private getTab(type: TabId, label: string): Tab<TabId> { private getTab(type: TabId, label: TranslationKey): Tab<TabId> {
const sources = this.state.sources const sources = this.state.sources
.filter((source) => source.id.startsWith(type)) .filter((source) => source.id.startsWith(type))
.map((source) => { .map((source) => {
@ -154,8 +154,8 @@ export default class DesktopCapturerSourcePicker extends React.Component<PickerI
public render(): React.ReactNode { public render(): React.ReactNode {
const tabs: NonEmptyArray<Tab<TabId>> = [ const tabs: NonEmptyArray<Tab<TabId>> = [
this.getTab("screen", _t("Share entire screen")), this.getTab("screen", _td("Share entire screen")),
this.getTab("window", _t("Application window")), this.getTab("window", _td("Application window")),
]; ];
return ( return (

View file

@ -85,8 +85,7 @@ export default class ErrorBoundary extends React.PureComponent<Props, IState> {
<React.Fragment> <React.Fragment>
<p> <p>
{_t( {_t(
"Please <newIssueLink>create a new issue</newIssueLink> " + "Please <newIssueLink>create a new issue</newIssueLink> on GitHub so that we can investigate this bug.",
"on GitHub so that we can investigate this bug.",
{}, {},
{ {
newIssueLink: (sub) => { newIssueLink: (sub) => {
@ -101,15 +100,10 @@ export default class ErrorBoundary extends React.PureComponent<Props, IState> {
</p> </p>
<p> <p>
{_t( {_t(
"If you've submitted a bug via GitHub, debug logs can help " + "If you've submitted a bug via GitHub, debug logs can help us track down the problem. ",
"us track down the problem. ",
)} )}
{_t( {_t(
"Debug logs contain application " + "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.",
"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.",
)} )}
</p> </p>
<AccessibleButton onClick={this.onBugReport} kind="primary"> <AccessibleButton onClick={this.onBugReport} kind="primary">

View file

@ -205,8 +205,7 @@ export default class ReplyChain extends React.Component<IProps, IState> {
header = ( header = (
<blockquote className="mx_ReplyChain mx_ReplyChain_error"> <blockquote className="mx_ReplyChain mx_ReplyChain_error">
{_t( {_t(
"Unable to load event that was replied to, " + "Unable to load event that was replied to, it either does not exist or you do not have permission to view it.",
"it either does not exist or you do not have permission to view it.",
)} )}
</blockquote> </blockquote>
); );

View file

@ -47,9 +47,7 @@ const onHelpClick = (): void => {
{ {
title: _t("Server Options"), title: _t("Server Options"),
description: _t( description: _t(
"You can use the custom server options to sign into other Matrix servers by specifying " + "You can use the custom server options to sign into other Matrix servers by specifying a different homeserver URL. This allows you to use %(brand)s with an existing Matrix account on a different homeserver.",
"a different homeserver URL. This allows you to use %(brand)s with an existing Matrix account on " +
"a different homeserver.",
{ brand }, { brand },
), ),
button: _t("Dismiss"), button: _t("Dismiss"),

View file

@ -29,7 +29,7 @@ interface IProps {
name: string; name: string;
level: SettingLevel; level: SettingLevel;
roomId?: string; // for per-room settings roomId?: string; // for per-room settings
label?: string; // untranslated label?: string;
isExplicit?: boolean; isExplicit?: boolean;
// XXX: once design replaces all toggles make this the default // XXX: once design replaces all toggles make this the default
useCheckbox?: boolean; useCheckbox?: boolean;
@ -105,10 +105,7 @@ export default class SettingsFlag extends React.Component<IProps, IState> {
if (!canChange && this.props.hideIfCannotSet) return null; if (!canChange && this.props.hideIfCannotSet) return null;
const label = const label = this.props.label ?? SettingsStore.getDisplayName(this.props.name, this.props.level);
(this.props.label
? _t(this.props.label)
: SettingsStore.getDisplayName(this.props.name, this.props.level)) ?? undefined;
const description = SettingsStore.getDescription(this.props.name); const description = SettingsStore.getDescription(this.props.name);
const shouldWarn = SettingsStore.shouldHaveWarning(this.props.name); const shouldWarn = SettingsStore.shouldHaveWarning(this.props.name);
const disabled = this.state.disabled || !canChange; const disabled = this.state.disabled || !canChange;
@ -146,7 +143,7 @@ export default class SettingsFlag extends React.Component<IProps, IState> {
onChange={this.onChange} onChange={this.onChange}
disabled={disabled} disabled={disabled}
tooltip={disabled ? SettingsStore.disabledMessage(this.props.name) : undefined} tooltip={disabled ? SettingsStore.disabledMessage(this.props.name) : undefined}
title={label} title={label ?? undefined}
/> />
</div> </div>
); );

View file

@ -36,10 +36,7 @@ export const EnableLiveShare: React.FC<Props> = ({ onSubmit }) => {
</Heading> </Heading>
<p className="mx_EnableLiveShare_description"> <p className="mx_EnableLiveShare_description">
{_t( {_t(
"Please note: this is a labs feature using a temporary implementation. " + "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.",
"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.",
)} )}
</p> </p>
<LabelledToggleSwitch <LabelledToggleSwitch

View file

@ -167,16 +167,12 @@ export default class DateSeparator extends React.Component<IProps, IState> {
let submitDebugLogsContent: JSX.Element = <></>; let submitDebugLogsContent: JSX.Element = <></>;
if (err instanceof ConnectionError) { if (err instanceof ConnectionError) {
friendlyErrorMessage = _t( friendlyErrorMessage = _t(
"A network error occurred while trying to find and jump to the given date. " + "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.",
"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) { } else if (err instanceof MatrixError) {
if (err?.errcode === "M_NOT_FOUND") { if (err?.errcode === "M_NOT_FOUND") {
friendlyErrorMessage = _t( friendlyErrorMessage = _t(
"We were unable to find an event looking forwards from %(dateString)s. " + "We were unable to find an event looking forwards from %(dateString)s. Try choosing an earlier date.",
"Try choosing an earlier date.",
{ dateString: formatFullDateNoDay(new Date(unixTimestamp)) }, { dateString: formatFullDateNoDay(new Date(unixTimestamp)) },
); );
} else { } else {
@ -192,8 +188,7 @@ export default class DateSeparator extends React.Component<IProps, IState> {
submitDebugLogsContent = ( submitDebugLogsContent = (
<p> <p>
{_t( {_t(
"Please submit <debugLogsLink>debug logs</debugLogsLink> to help us " + "Please submit <debugLogsLink>debug logs</debugLogsLink> to help us track down the problem.",
"track down the problem.",
{}, {},
{ {
debugLogsLink: (sub) => ( debugLogsLink: (sub) => (

View file

@ -22,7 +22,7 @@ import { Icon as DownloadIcon } from "../../../../res/img/download.svg";
import { MediaEventHelper } from "../../../utils/MediaEventHelper"; import { MediaEventHelper } from "../../../utils/MediaEventHelper";
import { RovingAccessibleTooltipButton } from "../../../accessibility/RovingTabIndex"; import { RovingAccessibleTooltipButton } from "../../../accessibility/RovingTabIndex";
import Spinner from "../elements/Spinner"; import Spinner from "../elements/Spinner";
import { _t, _td } from "../../../languageHandler"; import { _t, _td, TranslationKey } from "../../../languageHandler";
import { FileDownloader } from "../../../utils/FileDownloader"; import { FileDownloader } from "../../../utils/FileDownloader";
interface IProps { interface IProps {
@ -37,7 +37,7 @@ interface IProps {
interface IState { interface IState {
loading: boolean; loading: boolean;
blob?: Blob; blob?: Blob;
tooltip: string; tooltip: TranslationKey;
} }
export default class DownloadActionButton extends React.PureComponent<IProps, IState> { export default class DownloadActionButton extends React.PureComponent<IProps, IState> {

View file

@ -53,16 +53,14 @@ const EncryptionEvent = forwardRef<HTMLDivElement, IProps>(({ mxEvent, timestamp
} else if (dmPartner) { } else if (dmPartner) {
const displayName = room?.getMember(dmPartner)?.rawDisplayName || dmPartner; const displayName = room?.getMember(dmPartner)?.rawDisplayName || dmPartner;
subtitle = _t( subtitle = _t(
"Messages here are end-to-end encrypted. " + "Messages here are end-to-end encrypted. Verify %(displayName)s in their profile - tap on their profile picture.",
"Verify %(displayName)s in their profile - tap on their profile picture.",
{ displayName }, { displayName },
); );
} else if (room && isLocalRoom(room)) { } else if (room && isLocalRoom(room)) {
subtitle = _t("Messages in this chat will be end-to-end encrypted."); subtitle = _t("Messages in this chat will be end-to-end encrypted.");
} else { } else {
subtitle = _t( subtitle = _t(
"Messages in this room are end-to-end encrypted. " + "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.",
"When people join, you can verify them in their profile, just tap on their profile picture.",
); );
} }

View file

@ -106,9 +106,7 @@ export const RoomPredecessorTile: React.FC<IProps> = ({ mxEvent, timestamp }) =>
{!!guessedLink ? ( {!!guessedLink ? (
<> <>
{_t( {_t(
"Can't find the old version of this room (room ID: %(roomId)s), and we have not been " + "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:",
"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, roomId: predecessor.roomId,
}, },
@ -117,8 +115,7 @@ export const RoomPredecessorTile: React.FC<IProps> = ({ mxEvent, timestamp }) =>
</> </>
) : ( ) : (
_t( _t(
"Can't find the old version of this room (room ID: %(roomId)s), and we have not been " + "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.",
"provided with 'via_servers' to look for it.",
{ {
roomId: predecessor.roomId, roomId: predecessor.roomId,
}, },

View file

@ -494,9 +494,7 @@ export default class TextualBody extends React.Component<IBodyProps, IState> {
description: ( description: (
<div> <div>
{_t( {_t(
"You are about to be taken to a third-party site so you can " + "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?",
"authenticate your account for use with %(integrationsUrl)s. " +
"Do you wish to continue?",
{ integrationsUrl: integrationsUrl }, { integrationsUrl: integrationsUrl },
)} )}
</div> </div>

View file

@ -81,8 +81,7 @@ const EncryptionInfo: React.FC<IProps> = ({
<p>{_t("Messages in this room are end-to-end encrypted.")}</p> <p>{_t("Messages in this room are end-to-end encrypted.")}</p>
<p> <p>
{_t( {_t(
"Your messages are secured and only you and the recipient have " + "Your messages are secured and only you and the recipient have the unique keys to unlock them.",
"the unique keys to unlock them.",
)} )}
</p> </p>
</div> </div>
@ -93,8 +92,7 @@ const EncryptionInfo: React.FC<IProps> = ({
<p>{_t("Messages in this room are not end-to-end encrypted.")}</p> <p>{_t("Messages in this room are not end-to-end encrypted.")}</p>
<p> <p>
{_t( {_t(
"In encrypted rooms, your messages are secured and only you and the recipient have " + "In encrypted rooms, your messages are secured and only you and the recipient have the unique keys to unlock them.",
"the unique keys to unlock them.",
)} )}
</p> </p>
</div> </div>

View file

@ -181,8 +181,7 @@ const PinnedMessagesCard: React.FC<IProps> = ({ room, onClose, permalinkCreator
{_t("Nothing pinned, yet")} {_t("Nothing pinned, yet")}
</Heading> </Heading>
{_t( {_t(
"If you have permissions, open the menu on any message and select " + "If you have permissions, open the menu on any message and select <b>Pin</b> to stick them here.",
"<b>Pin</b> to stick them here.",
{}, {},
{ {
b: (sub) => <b>{sub}</b>, b: (sub) => <b>{sub}</b>,

View file

@ -383,8 +383,7 @@ export const UserOptionsSection: React.FC<{
description: ( description: (
<div> <div>
{_t( {_t(
"All messages and invites from this user will be hidden. " + "All messages and invites from this user will be hidden. Are you sure you want to ignore them?",
"Are you sure you want to ignore them?",
)} )}
</div> </div>
), ),
@ -523,14 +522,10 @@ export const warnSelfDemote = async (isSpace: boolean): Promise<boolean> => {
<div> <div>
{isSpace {isSpace
? _t( ? _t(
"You will not be able to undo this change as you are demoting yourself, " + "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.",
"if you are the last privileged user in the space it will be impossible " +
"to regain privileges.",
) )
: _t( : _t(
"You will not be able to undo this change as you are demoting yourself, " + "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.",
"if you are the last privileged user in the room it will be impossible " +
"to regain privileges.",
)} )}
</div> </div>
), ),
@ -1185,8 +1180,7 @@ export const PowerLevelEditor: React.FC<{
description: ( description: (
<div> <div>
{_t( {_t(
"You will not be able to undo this change as you are promoting the user " + "You will not be able to undo this change as you are promoting the user to have the same power level as yourself.",
"to have the same power level as yourself.",
)} )}
<br /> <br />
{_t("Are you sure?")} {_t("Are you sure?")}
@ -1355,9 +1349,7 @@ const BasicUserInfo: React.FC<{
description: ( description: (
<div> <div>
{_t( {_t(
"Deactivating this user will log them out and prevent them from logging back in. Additionally, " + "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?",
"they will leave all the rooms they are in. This action cannot be reversed. Are you sure you " +
"want to deactivate this user?",
)} )}
</div> </div>
), ),

View file

@ -93,9 +93,7 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
!showSAS && !showQR ? ( !showSAS && !showQR ? (
<p> <p>
{_t( {_t(
"The device you are trying to verify doesn't support scanning a " + "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.",
"QR code or emoji verification, which is what %(brand)s supports. Try " +
"with a different client.",
{ brand }, { brand },
)} )}
</p> </p>

View file

@ -182,8 +182,7 @@ export default class AliasSettings extends React.Component<IProps, IState> {
Modal.createDialog(ErrorDialog, { Modal.createDialog(ErrorDialog, {
title: _t("Error updating main address"), title: _t("Error updating main address"),
description: _t( description: _t(
"There was an error updating the room's main address. It may not be allowed by the server " + "There was an error updating the room's main address. It may not be allowed by the server or a temporary failure occurred.",
"or a temporary failure occurred.",
), ),
}); });
this.setState({ canonicalAlias: oldAlias }); this.setState({ canonicalAlias: oldAlias });
@ -222,8 +221,7 @@ export default class AliasSettings extends React.Component<IProps, IState> {
Modal.createDialog(ErrorDialog, { Modal.createDialog(ErrorDialog, {
title: _t("Error updating main address"), title: _t("Error updating main address"),
description: _t( description: _t(
"There was an error updating the room's alternative addresses. " + "There was an error updating the room's alternative addresses. It may not be allowed by the server or a temporary failure occurred.",
"It may not be allowed by the server or a temporary failure occurred.",
), ),
}); });
}) })
@ -258,8 +256,7 @@ export default class AliasSettings extends React.Component<IProps, IState> {
Modal.createDialog(ErrorDialog, { Modal.createDialog(ErrorDialog, {
title: _t("Error creating address"), title: _t("Error creating address"),
description: _t( description: _t(
"There was an error creating that address. It may not be allowed by the server " + "There was an error creating that address. It may not be allowed by the server or a temporary failure occurred.",
"or a temporary failure occurred.",
), ),
}); });
}); });
@ -286,8 +283,7 @@ export default class AliasSettings extends React.Component<IProps, IState> {
description = _t("You don't have permission to delete the address."); description = _t("You don't have permission to delete the address.");
} else { } else {
description = _t( description = _t(
"There was an error removing that address. It may no longer exist or a temporary " + "There was an error removing that address. It may no longer exist or a temporary error occurred.",
"error occurred.",
); );
} }
Modal.createDialog(ErrorDialog, { Modal.createDialog(ErrorDialog, {
@ -450,13 +446,11 @@ export default class AliasSettings extends React.Component<IProps, IState> {
description={ description={
isSpaceRoom isSpaceRoom
? _t( ? _t(
"Set addresses for this space so users can find this space " + "Set addresses for this space so users can find this space through your homeserver (%(localDomain)s)",
"through your homeserver (%(localDomain)s)",
{ localDomain }, { localDomain },
) )
: _t( : _t(
"Set addresses for this room so users can find this room " + "Set addresses for this room so users can find this room through your homeserver (%(localDomain)s)",
"through your homeserver (%(localDomain)s)",
{ localDomain }, { localDomain },
) )
} }

View file

@ -95,9 +95,7 @@ export default class UrlPreviewSettings extends React.Component<IProps> {
} }
} else { } else {
previewsForAccount = _t( previewsForAccount = _t(
"In encrypted rooms, like this one, URL previews are disabled by default to ensure that your " + "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.",
"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<IProps> {
<> <>
<p> <p>
{_t( {_t(
"When someone puts a URL in their message, a URL preview can be shown to give more " + "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.",
"information about that link such as the title, description, and an image from the website.",
)} )}
</p> </p>
<p>{previewsForAccount}</p> <p>{previewsForAccount}</p>

View file

@ -18,7 +18,7 @@ limitations under the License.
import React, { CSSProperties, useState } from "react"; import React, { CSSProperties, useState } from "react";
import classNames from "classnames"; import classNames from "classnames";
import { _t, _td } from "../../../languageHandler"; import { _t, _td, TranslationKey } from "../../../languageHandler";
import AccessibleButton from "../elements/AccessibleButton"; import AccessibleButton from "../elements/AccessibleButton";
import Tooltip, { Alignment } from "../elements/Tooltip"; import Tooltip, { Alignment } from "../elements/Tooltip";
import { E2EStatus } from "../../../utils/ShieldUtils"; import { E2EStatus } from "../../../utils/ShieldUtils";
@ -32,12 +32,12 @@ export enum E2EState {
Unauthenticated = "unauthenticated", 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.Warning]: _td("This user has not verified all of their sessions."),
[E2EState.Normal]: _td("You have not verified this user."), [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."), [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.Warning]: _td("Someone is using an unknown session"),
[E2EState.Normal]: _td("This room is end-to-end encrypted"), [E2EState.Normal]: _td("This room is end-to-end encrypted"),
[E2EState.Verified]: _td("Everyone in this room is verified"), [E2EState.Verified]: _td("Everyone in this room is verified"),
@ -85,7 +85,7 @@ const E2EIcon: React.FC<XOR<UserProps, RoomProps>> = ({
className, className,
); );
let e2eTitle: string | undefined; let e2eTitle: TranslationKey | undefined;
if (isUser) { if (isUser) {
e2eTitle = crossSigningUserTitles[status]; e2eTitle = crossSigningUserTitles[status];
} else { } else {

View file

@ -20,7 +20,7 @@ import React from "react";
import classNames from "classnames"; import classNames from "classnames";
import AccessibleButton from "../elements/AccessibleButton"; import AccessibleButton from "../elements/AccessibleButton";
import { _t, _td } from "../../../languageHandler"; import { _t, _td, TranslationKey } from "../../../languageHandler";
import E2EIcon, { E2EState } from "./E2EIcon"; import E2EIcon, { E2EState } from "./E2EIcon";
import BaseAvatar from "../avatars/BaseAvatar"; import BaseAvatar from "../avatars/BaseAvatar";
import PresenceLabel from "./PresenceLabel"; import PresenceLabel from "./PresenceLabel";
@ -30,7 +30,7 @@ export enum PowerStatus {
Moderator = "moderator", Moderator = "moderator",
} }
const PowerLabel: Record<PowerStatus, string> = { const PowerLabel: Record<PowerStatus, TranslationKey> = {
[PowerStatus.Admin]: _td("Admin"), [PowerStatus.Admin]: _td("Admin"),
[PowerStatus.Moderator]: _td("Mod"), [PowerStatus.Moderator]: _td("Mod"),
}; };

View file

@ -20,7 +20,7 @@ import { EventType, Room, User, MatrixClient } from "matrix-js-sdk/src/matrix";
import MatrixClientContext from "../../../contexts/MatrixClientContext"; import MatrixClientContext from "../../../contexts/MatrixClientContext";
import RoomContext from "../../../contexts/RoomContext"; import RoomContext from "../../../contexts/RoomContext";
import DMRoomMap from "../../../utils/DMRoomMap"; import DMRoomMap from "../../../utils/DMRoomMap";
import { _t, _td } from "../../../languageHandler"; import { _t, _td, TranslationKey } from "../../../languageHandler";
import AccessibleButton, { ButtonEvent } from "../elements/AccessibleButton"; import AccessibleButton, { ButtonEvent } from "../elements/AccessibleButton";
import MiniAvatarUploader, { AVATAR_SIZE } from "../elements/MiniAvatarUploader"; import MiniAvatarUploader, { AVATAR_SIZE } from "../elements/MiniAvatarUploader";
import RoomAvatar from "../avatars/RoomAvatar"; import RoomAvatar from "../avatars/RoomAvatar";
@ -44,7 +44,7 @@ function hasExpectedEncryptionSettings(matrixClient: MatrixClient, room: Room):
return isPublic || !privateShouldBeEncrypted(matrixClient) || isEncrypted; return isPublic || !privateShouldBeEncrypted(matrixClient) || isEncrypted;
} }
const determineIntroMessage = (room: Room, encryptedSingle3rdPartyInvite: boolean): string => { const determineIntroMessage = (room: Room, encryptedSingle3rdPartyInvite: boolean): TranslationKey => {
if (room instanceof LocalRoom) { if (room instanceof LocalRoom) {
return _td("Send your first message to invite <displayName/> to chat"); return _td("Send your first message to invite <displayName/> to chat");
} }
@ -270,9 +270,7 @@ const NewRoomIntro: React.FC = () => {
} }
const subText = _t( const subText = _t(
"Your private messages are normally encrypted, but this room isn'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.",
"Usually this is due to an unsupported device or method being used, " +
"like email invites.",
); );
let subButton: JSX.Element | undefined; let subButton: JSX.Element | undefined;

View file

@ -26,7 +26,7 @@ import { ActionPayload } from "../../../dispatcher/payloads";
import { ViewRoomDeltaPayload } from "../../../dispatcher/payloads/ViewRoomDeltaPayload"; import { ViewRoomDeltaPayload } from "../../../dispatcher/payloads/ViewRoomDeltaPayload";
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload"; import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
import { useEventEmitterState } from "../../../hooks/useEventEmitter"; import { useEventEmitterState } from "../../../hooks/useEventEmitter";
import { _t, _td } from "../../../languageHandler"; import { _t, _td, TranslationKey } from "../../../languageHandler";
import { MatrixClientPeg } from "../../../MatrixClientPeg"; import { MatrixClientPeg } from "../../../MatrixClientPeg";
import PosthogTrackers from "../../../PosthogTrackers"; import PosthogTrackers from "../../../PosthogTrackers";
import SettingsStore from "../../../settings/SettingsStore"; import SettingsStore from "../../../settings/SettingsStore";
@ -93,7 +93,7 @@ export const TAG_ORDER: TagID[] = [
const ALWAYS_VISIBLE_TAGS: TagID[] = [DefaultTagID.DM, DefaultTagID.Untagged]; const ALWAYS_VISIBLE_TAGS: TagID[] = [DefaultTagID.DM, DefaultTagID.Untagged];
interface ITagAesthetics { interface ITagAesthetics {
sectionLabel: string; sectionLabel: TranslationKey;
sectionLabelRaw?: string; sectionLabelRaw?: string;
AuxButtonComponent?: ComponentType<IAuxButtonProps>; AuxButtonComponent?: ComponentType<IAuxButtonProps>;
isInvite: boolean; isInvite: boolean;

View file

@ -421,8 +421,7 @@ export default class RoomPreviewBar extends React.Component<IProps, IState> {
} }
const joinRule = this.joinRule(); const joinRule = this.joinRule();
const errCodeMessage = _t( const errCodeMessage = _t(
"An error (%(errcode)s) was returned while trying to validate your " + "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.",
"invite. You could try to pass this information on to the person who invited you.",
{ errcode: this.state.threePidFetchError?.errcode || _t("unknown error code") }, { errcode: this.state.threePidFetchError?.errcode || _t("unknown error code") },
); );
switch (joinRule) { switch (joinRule) {
@ -447,8 +446,7 @@ export default class RoomPreviewBar extends React.Component<IProps, IState> {
case MessageCase.InvitedEmailNotFoundInAccount: { case MessageCase.InvitedEmailNotFoundInAccount: {
if (roomName) { if (roomName) {
title = _t( title = _t(
"This invite to %(roomName)s was sent to %(email)s which is not " + "This invite to %(roomName)s was sent to %(email)s which is not associated with your account",
"associated with your account",
{ {
roomName, roomName,
email: this.props.invitedEmail, email: this.props.invitedEmail,
@ -585,9 +583,7 @@ export default class RoomPreviewBar extends React.Component<IProps, IState> {
subTitle = [ subTitle = [
_t("Try again later, or ask a room or space admin to check if you have access."), _t("Try again later, or ask a room or space admin to check if you have access."),
_t( _t(
"%(errcode)s was returned while trying to access the room or space. " + "%(errcode)s was returned while trying to access the room or space. If you think you're seeing this message in error, please <issueLink>submit a bug report</issueLink>.",
"If you think you're seeing this message in error, please " +
"<issueLink>submit a bug report</issueLink>.",
{ errcode: String(this.props.error?.errcode) }, { errcode: String(this.props.error?.errcode) },
{ {
issueLink: (label) => ( issueLink: (label) => (

View file

@ -73,15 +73,12 @@ export default class RoomUpgradeWarningBar extends React.PureComponent<IProps, I
<div className="mx_RoomUpgradeWarningBar_body"> <div className="mx_RoomUpgradeWarningBar_body">
<p> <p>
{_t( {_t(
"Upgrading this room will shut down the current instance of the room and create " + "Upgrading this room will shut down the current instance of the room and create an upgraded room with the same name.",
"an upgraded room with the same name.",
)} )}
</p> </p>
<p> <p>
{_t( {_t(
"<b>Warning</b>: upgrading a room will <i>not automatically migrate room members " + "<b>Warning</b>: upgrading a room will <i>not automatically migrate room members to the new version of the room.</i> 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.",
"to the new version of the room.</i> 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) => <b>{sub}</b>, b: (sub) => <b>{sub}</b>,
@ -111,8 +108,7 @@ export default class RoomUpgradeWarningBar extends React.PureComponent<IProps, I
<div className="mx_RoomUpgradeWarningBar_wrapped"> <div className="mx_RoomUpgradeWarningBar_wrapped">
<div className="mx_RoomUpgradeWarningBar_header"> <div className="mx_RoomUpgradeWarningBar_header">
{_t( {_t(
"This room is running room version <roomVersion />, which this homeserver has " + "This room is running room version <roomVersion />, which this homeserver has marked as <i>unstable</i>.",
"marked as <i>unstable</i>.",
{}, {},
{ {
roomVersion: () => <code>{this.props.room.getVersion()}</code>, roomVersion: () => <code>{this.props.room.getVersion()}</code>,

View file

@ -496,7 +496,10 @@ export class SendMessageComposer extends React.Component<ISendMessageComposerPro
return; // errored return; // errored
} }
if (content && [CommandCategories.messages, CommandCategories.effects].includes(cmd.category)) { if (
content &&
[CommandCategories.messages as string, CommandCategories.effects as string].includes(cmd.category)
) {
// Attach any mentions which might be contained in the command content. // Attach any mentions which might be contained in the command content.
attachMentions(this.props.mxClient.getSafeUserId(), content, model, replyToEvent); attachMentions(this.props.mxClient.getSafeUserId(), content, model, replyToEvent);
attachRelation(content, this.props.relation); attachRelation(content, this.props.relation);

View file

@ -19,7 +19,7 @@ import { Room, ClientEvent } from "matrix-js-sdk/src/matrix";
import { logger } from "matrix-js-sdk/src/logger"; import { logger } from "matrix-js-sdk/src/logger";
import { IWidget } from "matrix-widget-api"; import { IWidget } from "matrix-widget-api";
import { _t, _td } from "../../../languageHandler"; import { _t, _td, TranslationKey } from "../../../languageHandler";
import AppTile from "../elements/AppTile"; import AppTile from "../elements/AppTile";
import { MatrixClientPeg } from "../../../MatrixClientPeg"; import { MatrixClientPeg } from "../../../MatrixClientPeg";
import dis from "../../../dispatcher/dispatcher"; import dis from "../../../dispatcher/dispatcher";
@ -159,7 +159,7 @@ export default class Stickerpicker extends React.PureComponent<IProps, IState> {
this.sendVisibilityToWidget(this.props.isStickerPickerOpen); this.sendVisibilityToWidget(this.props.isStickerPickerOpen);
} }
private imError(errorMsg: string, e: Error): void { private imError(errorMsg: TranslationKey, e: Error): void {
logger.error(errorMsg, e); logger.error(errorMsg, e);
this.setState({ this.setState({
imError: _t(errorMsg), imError: _t(errorMsg),

View file

@ -108,8 +108,7 @@ export default class ThirdPartyMemberInfo extends React.Component<IProps, IState
Modal.createDialog(ErrorDialog, { Modal.createDialog(ErrorDialog, {
title: _t("Failed to revoke invite"), title: _t("Failed to revoke invite"),
description: _t( description: _t(
"Could not revoke the invite. The server may be experiencing a temporary problem or " + "Could not revoke the invite. The server may be experiencing a temporary problem or you do not have sufficient permissions to revoke the invite.",
"you do not have sufficient permissions to revoke the invite.",
), ),
}); });
}); });

View file

@ -203,8 +203,7 @@ export default class CrossSigningPanel extends React.PureComponent<{}, IState> {
summarisedStatus = ( summarisedStatus = (
<SettingsSubsectionText data-testid="summarised-status"> <SettingsSubsectionText data-testid="summarised-status">
{_t( {_t(
"Your account has a cross-signing identity in secret storage, " + "Your account has a cross-signing identity in secret storage, but it is not yet trusted by this session.",
"but it is not yet trusted by this session.",
)} )}
</SettingsSubsectionText> </SettingsSubsectionText>
); );

View file

@ -149,8 +149,7 @@ export default class EventIndexPanel extends React.Component<{}, IState> {
<> <>
<SettingsSubsectionText> <SettingsSubsectionText>
{_t( {_t(
"Securely cache encrypted messages locally for them " + "Securely cache encrypted messages locally for them to appear in search results, using %(size)s to store messages from %(rooms)s rooms.",
"to appear in search results, using %(size)s to store messages from %(rooms)s rooms.",
{ {
size: formatBytes(this.state.eventIndexSize, 0), size: formatBytes(this.state.eventIndexSize, 0),
// This drives the singular / plural string // This drives the singular / plural string
@ -188,10 +187,7 @@ export default class EventIndexPanel extends React.Component<{}, IState> {
eventIndexingSettings = ( eventIndexingSettings = (
<SettingsSubsectionText> <SettingsSubsectionText>
{_t( {_t(
"%(brand)s is missing some components required for securely " + "%(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 <nativeLink>search components added</nativeLink>.",
"caching encrypted messages locally. If you'd like to " +
"experiment with this feature, build a custom %(brand)s Desktop " +
"with <nativeLink>search components added</nativeLink>.",
{ {
brand, brand,
}, },
@ -209,9 +205,7 @@ export default class EventIndexPanel extends React.Component<{}, IState> {
eventIndexingSettings = ( eventIndexingSettings = (
<SettingsSubsectionText> <SettingsSubsectionText>
{_t( {_t(
"%(brand)s can't securely cache encrypted messages locally " + "%(brand)s can't securely cache encrypted messages locally while running in a web browser. Use <desktopLink>%(brand)s Desktop</desktopLink> for encrypted messages to appear in search results.",
"while running in a web browser. Use <desktopLink>%(brand)s Desktop</desktopLink> " +
"for encrypted messages to appear in search results.",
{ {
brand, brand,
}, },

View file

@ -323,9 +323,7 @@ const JoinRuleSettings: React.FC<JoinRuleSettingsProps> = ({
warning = ( warning = (
<b> <b>
{_t( {_t(
"This room is in some spaces you're not an admin of. " + "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.",
"In those spaces, the old room will still be shown, " +
"but people will be prompted to join the new one.",
)} )}
</b> </b>
); );
@ -335,8 +333,7 @@ const JoinRuleSettings: React.FC<JoinRuleSettingsProps> = ({
targetVersion, targetVersion,
<> <>
{_t( {_t(
"This upgrade will allow members of selected spaces " + "This upgrade will allow members of selected spaces access to this room without an invite.",
"access to this room without an invite.",
)} )}
{warning} {warning}
</>, </>,

View file

@ -259,17 +259,14 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> {
<> <>
<SettingsSubsectionText> <SettingsSubsectionText>
{_t( {_t(
"This session is <b>not backing up your keys</b>, " + "This session is <b>not backing up your keys</b>, but you do have an existing backup you can restore from and add to going forward.",
"but you do have an existing backup you can restore from " +
"and add to going forward.",
{}, {},
{ b: (sub) => <b>{sub}</b> }, { b: (sub) => <b>{sub}</b> },
)} )}
</SettingsSubsectionText> </SettingsSubsectionText>
<SettingsSubsectionText> <SettingsSubsectionText>
{_t( {_t(
"Connect this session to key backup before signing out to avoid " + "Connect this session to key backup before signing out to avoid losing any keys that may only be on this session.",
"losing any keys that may only be on this session.",
)} )}
</SettingsSubsectionText> </SettingsSubsectionText>
</> </>
@ -382,9 +379,7 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> {
<> <>
<SettingsSubsectionText> <SettingsSubsectionText>
{_t( {_t(
"Back up your encryption keys with your account data in case you " + "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.",
"lose access to your sessions. Your keys will be secured with a " +
"unique Security Key.",
)} )}
</SettingsSubsectionText> </SettingsSubsectionText>
{statusDescription} {statusDescription}

Some files were not shown because too many files have changed in this diff Show more