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>;
// Utility type for string dot notation for accessing nested object properties
// Based on https://stackoverflow.com/a/58436959
type Join<K, P> = K extends string | number
/**
* Utility type for string dot notation for accessing nested object properties.
* Based on https://stackoverflow.com/a/58436959
* @example
* {
* "a": {
* "b": {
* "c": "value"
* },
* "d": "foobar"
* }
* }
* will yield a type of `"a.b.c" | "a.d"` with Separator="."
* @typeParam Target the target type to generate leaf keys for
* @typeParam Separator the separator to use between key segments when accessing nested objects
* @typeParam LeafType the type which leaves of this object extend, used to determine when to stop recursion
* @typeParam MaxDepth the maximum depth to recurse to
* @returns a union type representing all dot (Separator) string notation keys which can access a Leaf (of LeafType)
*/
export type Leaves<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
? `${K}${"" extends P ? "" : "."}${P}`
? `${K}${"" extends P ? "" : S}${P}`
: 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> = {
[P in keyof T]?: T[P] extends (infer U)[]
? RecursivePartial<U>[]

View file

@ -141,9 +141,7 @@ export default class IdentityAuthClient {
<div>
<p>
{_t(
"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.",
"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: () => <b>{abbreviateUrl(identityServerUrl)}</b>,

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -559,8 +559,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
<form onSubmit={this.onChooseKeyPassphraseFormSubmit}>
<p className="mx_CreateSecretStorageDialog_centeredBody">
{_t(
"Safeguard against losing access to encrypted messages & data by " +
"backing up encryption keys on your server.",
"Safeguard against losing access to encrypted messages & data by backing up encryption keys on your server.",
)}
</p>
<div className="mx_CreateSecretStorageDialog_primaryContainer" role="radiogroup">
@ -611,9 +610,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
<form onSubmit={this.onMigrateFormSubmit}>
<p>
{_t(
"Upgrade this session to allow it to verify other sessions, " +
"granting them access to encrypted messages and marking them " +
"as trusted for other users.",
"Upgrade this session to allow it to verify other sessions, granting them access to encrypted messages and marking them as trusted for other users.",
)}
</p>
<div>{authPrompt}</div>
@ -636,8 +633,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
<form onSubmit={this.onPassPhraseNextClick}>
<p>
{_t(
"Enter a Security Phrase only you know, as it's used to safeguard your data. " +
"To be secure, you shouldn't re-use your account password.",
"Enter a Security Phrase only you know, as it's used to safeguard your data. To be secure, you shouldn't re-use your account password.",
)}
</p>
@ -752,8 +748,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
<div>
<p>
{_t(
"Store your Security Key somewhere safe, like a password manager or a safe, " +
"as it's used to safeguard your encrypted data.",
"Store your Security Key somewhere safe, like a password manager or a safe, as it's used to safeguard your encrypted data.",
)}
</p>
<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">
<p>
{_t(
"This process allows you to export the keys for messages " +
"you have received in encrypted rooms to a local file. You " +
"will then be able to import the file into another Matrix " +
"client in the future, so that client will also be able to " +
"decrypt these messages.",
"This process allows you to export the keys for messages you have received in encrypted rooms to a local file. You will then be able to import the file into another Matrix client in the future, so that client will also be able to decrypt these messages.",
)}
</p>
<p>
{_t(
"The exported file will allow anyone who can read it to decrypt " +
"any encrypted messages that you can see, so you should be " +
"careful to keep it secure. To help with this, you should enter " +
"a unique passphrase below, which will only be used to encrypt the " +
"exported data. " +
"It will only be possible to import the data by using the same passphrase.",
"The exported file will allow anyone who can read it to decrypt any encrypted messages that you can see, so you should be careful to keep it secure. To help with this, you should enter a unique passphrase below, which will only be used to encrypt the exported data. It will only be possible to import the data by using the same passphrase.",
)}
</p>
<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">
<p>
{_t(
"This process allows you to import encryption keys " +
"that you had previously exported from another Matrix " +
"client. You will then be able to decrypt any " +
"messages that the other client could decrypt.",
"This process allows you to import encryption keys that you had previously exported from another Matrix client. You will then be able to decrypt any messages that the other client could decrypt.",
)}
</p>
<p>
{_t(
"The export file will be protected with a passphrase. " +
"You should enter the passphrase here, to decrypt the file.",
"The export file will be protected with a passphrase. You should enter the passphrase here, to decrypt the file.",
)}
</p>
<div className="error">{this.state.errStr}</div>

View file

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

View file

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

View file

@ -21,7 +21,7 @@ import sanitizeHtml from "sanitize-html";
import classnames from "classnames";
import { logger } from "matrix-js-sdk/src/logger";
import { _t } from "../../languageHandler";
import { _t, TranslationKey } from "../../languageHandler";
import dis from "../../dispatcher/dispatcher";
import { MatrixClientPeg } from "../../MatrixClientPeg";
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));
}

View file

@ -482,12 +482,10 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
const waitText = _t("Wait!");
const scamText = _t(
"If someone told you to copy/paste something here, " + "there is a high likelihood you're being scammed!",
"If someone told you to copy/paste something here, there is a high likelihood you're being scammed!",
);
const devText = _t(
"If you know what you're doing, Element is open-source, " +
"be sure to check out our GitHub (https://github.com/vector-im/element-web/) " +
"and contribute!",
"If you know what you're doing, Element is open-source, be sure to check out our GitHub (https://github.com/vector-im/element-web/) and contribute!",
);
global.mx_rage_logger.bypassRageshake(
@ -1166,8 +1164,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
<span className="warning" key="only_member_warning">
{" " /* Whitespace, otherwise the sentences get smashed together */}
{_t(
"You are the only person here. " +
"If you leave, no one will be able to join in the future, including you.",
"You are the only person here. If you leave, no one will be able to join in the future, including you.",
)}
</span>,
);
@ -1593,8 +1590,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
<p>
{" "}
{_t(
"To continue using the %(homeserverDomain)s homeserver " +
"you must review and agree to our terms and conditions.",
"To continue using the %(homeserverDomain)s homeserver you must review and agree to our terms and conditions.",
{ homeserverDomain: cli.getDomain() },
)}
</p>
@ -1643,13 +1639,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
Modal.createDialog(ErrorDialog, {
title: _t("Old cryptography data detected"),
description: _t(
"Data from an older version of %(brand)s has been detected. " +
"This will have caused end-to-end cryptography to malfunction " +
"in the older version. End-to-end encrypted messages exchanged " +
"recently whilst using the older version may not be decryptable " +
"in this version. This may also cause messages exchanged with this " +
"version to fail. If you experience problems, log out and back in " +
"again. To retain message history, export and re-import your keys.",
"Data from an older version of %(brand)s has been detected. This will have caused end-to-end cryptography to malfunction in the older version. End-to-end encrypted messages exchanged recently whilst using the older version may not be decryptable in this version. This may also cause messages exchanged with this version to fail. If you experience problems, log out and back in again. To retain message history, export and re-import your keys.",
{ brand: SdkConfig.get().brand },
),
});

View file

@ -207,8 +207,7 @@ export default class RoomStatusBar extends React.PureComponent<IProps, IState> {
}
if (consentError) {
title = _t(
"You can't send any messages until you review and agree to " +
"<consentLink>our terms and conditions</consentLink>.",
"You can't send any messages until you review and agree to <consentLink>our terms and conditions</consentLink>.",
{},
{
consentLink: (sub) => (
@ -224,16 +223,13 @@ export default class RoomStatusBar extends React.PureComponent<IProps, IState> {
resourceLimitError.data.admin_contact,
{
"monthly_active_user": _td(
"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.",
"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.",
),
"hs_disabled": _td(
"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.",
"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.",
),
"": _td(
"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.",
"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.",
),
},
);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -161,10 +161,7 @@ export default class SetupEncryptionBody extends React.Component<IProps, IState>
<div>
<p>
{_t(
"It looks like you don't have a Security Key or any other devices you can " +
"verify against. This device will not be able to access old encrypted messages. " +
"In order to verify your identity on this device, you'll need to reset " +
"your verification keys.",
"It looks like you don't have a Security Key or any other devices you can verify against. This device will not be able to access old encrypted messages. In order to verify your identity on this device, you'll need to reset your verification keys.",
)}
</p>
@ -234,8 +231,7 @@ export default class SetupEncryptionBody extends React.Component<IProps, IState>
message = (
<p>
{_t(
"Your new device is now verified. It has access to your " +
"encrypted messages, and other users will see it as trusted.",
"Your new device is now verified. It has access to your encrypted messages, and other users will see it as trusted.",
)}
</p>
);
@ -258,8 +254,7 @@ export default class SetupEncryptionBody extends React.Component<IProps, IState>
<div>
<p>
{_t(
"Without verifying, you won't have access to all your messages " +
"and may appear as untrusted to others.",
"Without verifying, you won't have access to all your messages and may appear as untrusted to others.",
)}
</p>
<div className="mx_CompleteSecurity_actionRow">
@ -277,16 +272,12 @@ export default class SetupEncryptionBody extends React.Component<IProps, IState>
<div>
<p>
{_t(
"Resetting your verification keys cannot be undone. After resetting, " +
"you won't have access to old encrypted messages, and any friends who " +
"have previously verified you will see security warnings until you " +
"re-verify with them.",
"Resetting your verification keys cannot be undone. After resetting, you won't have access to old encrypted messages, and any friends who have previously verified you will see security warnings until you re-verify with them.",
)}
</p>
<p>
{_t(
"Please only proceed if you're sure you've lost all of your other " +
"devices and your Security Key.",
"Please only proceed if you're sure you've lost all of your other devices and your Security Key.",
)}
</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
if (this.state.keyBackupNeeded) {
introText = _t(
"Regain access to your account and recover encryption keys stored in this session. " +
"Without them, you won't be able to read all of your secure messages in any session.",
"Regain access to your account and recover encryption keys stored in this session. Without them, you won't be able to read all of your secure messages in any session.",
);
}
@ -329,10 +328,7 @@ export default class SoftLogout extends React.Component<IProps, IState> {
// Default: assume unsupported/error
return (
<p>
{_t(
"You cannot sign in to your account. Please contact your " +
"homeserver admin for more information.",
)}
{_t("You cannot sign in to your account. Please contact your homeserver admin for more information.")}
</p>
);
}
@ -350,9 +346,7 @@ export default class SoftLogout extends React.Component<IProps, IState> {
<h2>{_t("Clear personal data")}</h2>
<p>
{_t(
"Warning: your personal data (including encryption keys) is still stored " +
"in this session. Clear it if you're finished using this session, or want to sign " +
"in to another account.",
"Warning: your personal data (including encryption keys) is still stored in this session. Clear it if you're finished using this session, or want to sign in to another account.",
)}
</p>
<div>

View file

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

View file

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

View file

@ -220,8 +220,7 @@ export class RecaptchaAuthEntry extends React.Component<IRecaptchaAuthEntryProps
let sitePublicKey: string | undefined;
if (!this.props.stageParams || !this.props.stageParams.public_key) {
errorText = _t(
"Missing captcha public key in homeserver configuration. Please report " +
"this to your homeserver administrator.",
"Missing captcha public key in homeserver configuration. Please report this to your homeserver administrator.",
);
} else {
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 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"> {
id?: string;
@ -27,9 +27,9 @@ interface IProps extends Omit<IInputProps, "onValidate" | "label" | "element"> {
value: string;
password: string; // The password we're confirming
label: string;
labelRequired: string;
labelInvalid: string;
label: TranslationKey;
labelRequired: TranslationKey;
labelInvalid: TranslationKey;
onChange(ev: React.FormEvent<HTMLElement>): void;
onValidate?(result: IValidationResult): void;

View file

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

View file

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

View file

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

View file

@ -20,7 +20,7 @@ import { Room, EventType } from "matrix-js-sdk/src/matrix";
import { sleep } from "matrix-js-sdk/src/utils";
import { logger } from "matrix-js-sdk/src/logger";
import { _t, _td } from "../../../languageHandler";
import { _t, _td, TranslationKey } from "../../../languageHandler";
import BaseDialog from "./BaseDialog";
import Dropdown from "../elements/Dropdown";
import SearchBox from "../../structures/SearchBox";
@ -357,7 +357,7 @@ export const AddExistingToSpace: React.FC<IAddExistingToSpaceProps> = ({
};
const defaultRendererFactory =
(title: string): Renderer =>
(title: TranslationKey): Renderer =>
(rooms, selectedToAdd, { scrollTop, height }, onChange) =>
(
<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_copy">
{_t(
"Help us identify issues and improve %(analyticsOwner)s by sharing anonymous usage data. " +
"To understand how people use multiple devices, we'll generate a random identifier, " +
"shared by your devices.",
"Help us identify issues and improve %(analyticsOwner)s by sharing anonymous usage data. To understand how people use multiple devices, we'll generate a random identifier, shared by your devices.",
{ analyticsOwner },
)}
</div>

View file

@ -224,10 +224,7 @@ export default class BugReportDialog extends React.Component<IProps, IState> {
{warning}
<p>
{_t(
"Debug logs contain application usage data including your " +
"username, the IDs or aliases of the rooms you " +
"have visited, which UI elements you last interacted with, " +
"and the usernames of other users. They do not contain messages.",
"Debug logs contain application usage data including your username, the IDs or aliases of the rooms you have visited, which UI elements you last interacted with, and the usernames of other users. They do not contain messages.",
)}
</p>
<p>
@ -273,10 +270,7 @@ export default class BugReportDialog extends React.Component<IProps, IState> {
onChange={this.onTextChange}
value={this.state.text}
placeholder={_t(
"If there is additional context that would help in " +
"analysing the issue, such as what you were doing at " +
"the time, room IDs, user IDs, etc., " +
"please include those things here.",
"If there is additional context that would help in analysing the issue, such as what you were doing at the time, room IDs, user IDs, etc., please include those things here.",
)}
/>
{progress}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -70,9 +70,7 @@ export default class EndPollDialog extends React.Component<IProps> {
<QuestionDialog
title={_t("End Poll")}
description={_t(
"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.",
"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.",
)}
button={_t("End Poll")}
onFinished={(endPoll: boolean) => this.onFinished(endPoll)}

View file

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

View file

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

View file

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

View file

@ -1133,9 +1133,7 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
return (
<div className="mx_InviteDialog_identityServer">
{_t(
"Use an identity server to invite by email. " +
"<default>Use the default (%(defaultIdentityServerName)s)</default> " +
"or manage in <settings>Settings</settings>.",
"Use an identity server to invite by email. <default>Use the default (%(defaultIdentityServerName)s)</default> or manage in <settings>Settings</settings>.",
{
defaultIdentityServerName: abbreviateUrl(defaultIdentityServerUrl),
},
@ -1158,7 +1156,7 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
return (
<div className="mx_InviteDialog_identityServer">
{_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) => (
@ -1349,23 +1347,21 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
if (isSpace) {
if (identityServersEnabled) {
helpTextUntranslated = _td(
"Invite someone using their name, email address, username " +
"(like <userId/>) or <a>share this space</a>.",
"Invite someone using their name, email address, username (like <userId/>) or <a>share this space</a>.",
);
} else {
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 {
if (identityServersEnabled) {
helpTextUntranslated = _td(
"Invite someone using their name, email address, username " +
"(like <userId/>) or <a>share this room</a>.",
"Invite someone using their name, email address, username (like <userId/>) or <a>share this room</a>.",
);
} else {
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 brand = SdkConfig.get().brand;
const description1 = _t(
"You've previously used %(brand)s on %(host)s with lazy loading of members enabled. " +
"In this version lazy loading is disabled. " +
"As the local cache is not compatible between these two settings, " +
"%(brand)s needs to resync your account.",
"You've previously used %(brand)s on %(host)s with lazy loading of members enabled. In this version lazy loading is disabled. As the local cache is not compatible between these two settings, %(brand)s needs to resync your account.",
{
brand,
host: props.host,
},
);
const description2 = _t(
"If the other version of %(brand)s is still open in another tab, " +
"please close it as using %(brand)s on the same host with both " +
"lazy loading enabled and disabled simultaneously will cause issues.",
"If the other version of %(brand)s is still open in another tab, please close it as using %(brand)s on the same host with both lazy loading enabled and disabled simultaneously will cause issues.",
{
brand,
},

View file

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

View file

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

View file

@ -138,16 +138,12 @@ export default class LogoutDialog extends React.Component<IProps, IState> {
<div>
<p>
{_t(
"Encrypted messages are secured with end-to-end encryption. " +
"Only you and the recipient(s) have the keys to read these messages.",
"Encrypted messages are secured with end-to-end encryption. Only you and the recipient(s) have the keys to read these messages.",
)}
</p>
<p>
{_t(
"When you sign out, these keys will be deleted from this device, " +
"which means you won't be able to read encrypted messages unless you " +
"have the keys for them on your other devices, or backed them up to the " +
"server.",
"When you sign out, these keys will be deleted from this device, which means you won't be able to read encrypted messages unless you have the keys for them on your other devices, or backed them up to the server.",
)}
</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>
{_t(
"Decide which spaces can access this room. " +
"If a space is selected, its members can find and join <RoomName/>.",
"Decide which spaces can access this room. If a space is selected, its members can find and join <RoomName/>.",
{},
{
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">
<p>
{_t(
"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>.",
"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: (sub) => <b>{sub}</b>,

View file

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

View file

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

View file

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

View file

@ -107,8 +107,7 @@ export default class ServerOfflineDialog extends React.PureComponent<IProps> {
<div className="mx_ServerOfflineDialog_content">
<p>
{_t(
"Your server isn't responding to some of your requests. " +
"Below are some of the most likely reasons.",
"Your server isn't responding to some of your requests. Below are some of the most likely reasons.",
)}
</p>
<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")}
<br />
{_t(
"If you do, please note that none of your messages will be deleted, " +
"but the search experience might be degraded for a few moments " +
"whilst the index is recreated",
"If you do, please note that none of your messages will be deleted, but the search experience might be degraded for a few moments whilst the index is recreated",
)}
</p>
</div>

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -18,7 +18,7 @@ limitations under the License.
import React, { ChangeEvent, ReactNode, useContext, useMemo, useRef, useState } from "react";
import { IContent, MatrixEvent } from "matrix-js-sdk/src/matrix";
import { _t, _td } from "../../../../languageHandler";
import { _t, _td, TranslationKey } from "../../../../languageHandler";
import Field from "../../elements/Field";
import BaseTool, { DevtoolsContext, IDevtoolsProps } from "./BaseTool";
import MatrixClientContext from "../../../../contexts/MatrixClientContext";
@ -37,7 +37,7 @@ interface IEventEditorProps extends Pick<IDevtoolsProps, "onBack"> {
interface IFieldDef {
id: string;
label: string; // _td
label: TranslationKey;
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 { useTypedEventEmitter, useTypedEventEmitterState } from "../../../../hooks/useEventEmitter";
import { _t, _td } from "../../../../languageHandler";
import { _t, _td, TranslationKey } from "../../../../languageHandler";
import MatrixClientContext from "../../../../contexts/MatrixClientContext";
import BaseTool, { DevtoolsContext, IDevtoolsProps } from "./BaseTool";
import { Tool } from "../DevtoolsDialog";
const PHASE_MAP: Record<Phase, string> = {
const PHASE_MAP: Record<Phase, TranslationKey> = {
[Phase.Unsent]: _td("Unsent"),
[Phase.Requested]: _td("Requested"),
[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(
"If you reset everything, you will restart with no trusted sessions, no trusted users, and " +
"might not be able to see past messages.",
"If you reset everything, you will restart with no trusted sessions, no trusted users, and might not be able to see past messages.",
)}
</p>
<DialogButtons
@ -329,8 +328,7 @@ export default class AccessSecretStorageDialog extends React.PureComponent<IProp
<div className="mx_AccessSecretStorageDialog_keyStatus">
{"\uD83D\uDC4E "}
{_t(
"Unable to access secret storage. " +
"Please verify that you entered the correct Security Phrase.",
"Unable to access secret storage. Please verify that you entered the correct Security Phrase.",
)}
</div>
);

View file

@ -44,10 +44,7 @@ export default class ConfirmDestroyCrossSigningDialog extends React.Component<IP
<div className="mx_ConfirmDestroyCrossSigningDialog_content">
<p>
{_t(
"Deleting cross-signing keys is permanent. " +
"Anyone you have verified with will see security alerts. " +
"You almost certainly don't want to do this, unless " +
"you've lost every device you can cross-sign from.",
"Deleting cross-signing keys is permanent. Anyone you have verified with will see security alerts. You almost certainly don't want to do this, unless you've lost every device you can cross-sign from.",
)}
</p>
</div>

View file

@ -351,8 +351,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent<IProps,
<div>
<p>
{_t(
"Backup could not be decrypted with this Security Key: " +
"please verify that you entered the correct Security Key.",
"Backup could not be decrypted with this Security Key: please verify that you entered the correct Security Key.",
)}
</p>
</div>
@ -363,8 +362,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent<IProps,
<div>
<p>
{_t(
"Backup could not be decrypted with this Security Phrase: " +
"please verify that you entered the correct Security Phrase.",
"Backup could not be decrypted with this Security Phrase: please verify that you entered the correct Security Phrase.",
)}
</p>
</div>
@ -418,8 +416,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent<IProps,
</p>
<p>
{_t(
"Access your secure message history and set up secure " +
"messaging by entering your Security Phrase.",
"Access your secure message history and set up secure messaging by entering your Security Phrase.",
)}
</p>
@ -441,9 +438,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent<IProps,
/>
</form>
{_t(
"If you've forgotten your Security Phrase you can " +
"<button1>use your Security Key</button1> or " +
"<button2>set up new recovery options</button2>",
"If you've forgotten your Security Phrase you can <button1>use your Security Key</button1> or <button2>set up new recovery options</button2>",
{},
{
button1: (s) => (
@ -493,8 +488,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent<IProps,
</p>
<p>
{_t(
"Access your secure message history and set up secure " +
"messaging by entering your Security Key.",
"Access your secure message history and set up secure messaging by entering your Security Key.",
)}
</p>
@ -516,8 +510,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent<IProps,
/>
</div>
{_t(
"If you've forgotten your Security Key you can " +
"<button>set up new recovery options</button>",
"If you've forgotten your Security Key you can <button>set up new recovery options</button>",
{},
{
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">
<h4>{_t("Some results may be hidden")}</h4>
<div className="mx_SpotlightDialog_otherSearches_messageSearchText">
{_t(
"If you can't find the room you're looking for, " +
"ask for an invite or create a new room.",
)}
{_t("If you can't find the room you're looking for, ask for an invite or create a new room.")}
</div>
<Option
id="mx_SpotlightDialog_button_createNewRoom"

View file

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

View file

@ -85,8 +85,7 @@ export default class ErrorBoundary extends React.PureComponent<Props, IState> {
<React.Fragment>
<p>
{_t(
"Please <newIssueLink>create a new issue</newIssueLink> " +
"on GitHub so that we can investigate this bug.",
"Please <newIssueLink>create a new issue</newIssueLink> on GitHub so that we can investigate this bug.",
{},
{
newIssueLink: (sub) => {
@ -101,15 +100,10 @@ export default class ErrorBoundary extends React.PureComponent<Props, IState> {
</p>
<p>
{_t(
"If you've submitted a bug via GitHub, debug logs can help " +
"us track down the problem. ",
"If you've submitted a bug via GitHub, debug logs can help us track down the problem. ",
)}
{_t(
"Debug logs contain application " +
"usage data including your username, the IDs or aliases of " +
"the rooms you have visited, which UI elements you " +
"last interacted with, and the usernames of other users. " +
"They do not contain messages.",
"Debug logs contain application usage data including your username, the IDs or aliases of the rooms you have visited, which UI elements you last interacted with, and the usernames of other users. They do not contain messages.",
)}
</p>
<AccessibleButton onClick={this.onBugReport} kind="primary">

View file

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

View file

@ -47,9 +47,7 @@ const onHelpClick = (): void => {
{
title: _t("Server Options"),
description: _t(
"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.",
"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.",
{ brand },
),
button: _t("Dismiss"),

View file

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

View file

@ -36,10 +36,7 @@ export const EnableLiveShare: React.FC<Props> = ({ onSubmit }) => {
</Heading>
<p className="mx_EnableLiveShare_description">
{_t(
"Please note: this is a labs feature using a temporary implementation. " +
"This means you will not be able to delete your location history, " +
"and advanced users will be able to see your location history " +
"even after you stop sharing your live location with this room.",
"Please note: this is a labs feature using a temporary implementation. This means you will not be able to delete your location history, and advanced users will be able to see your location history even after you stop sharing your live location with this room.",
)}
</p>
<LabelledToggleSwitch

View file

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

View file

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

View file

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

View file

@ -106,9 +106,7 @@ export const RoomPredecessorTile: React.FC<IProps> = ({ mxEvent, timestamp }) =>
{!!guessedLink ? (
<>
{_t(
"Can't find the old version of this room (room ID: %(roomId)s), and we have not been " +
"provided with 'via_servers' to look for it. It's possible that guessing the " +
"server from the room ID will work. If you want to try, click this link:",
"Can't find the old version of this room (room ID: %(roomId)s), and we have not been provided with 'via_servers' to look for it. It's possible that guessing the server from the room ID will work. If you want to try, click this link:",
{
roomId: predecessor.roomId,
},
@ -117,8 +115,7 @@ export const RoomPredecessorTile: React.FC<IProps> = ({ mxEvent, timestamp }) =>
</>
) : (
_t(
"Can't find the old version of this room (room ID: %(roomId)s), and we have not been " +
"provided with 'via_servers' to look for it.",
"Can't find the old version of this room (room ID: %(roomId)s), and we have not been provided with 'via_servers' to look for it.",
{
roomId: predecessor.roomId,
},

View file

@ -494,9 +494,7 @@ export default class TextualBody extends React.Component<IBodyProps, IState> {
description: (
<div>
{_t(
"You are about to be taken to a third-party site so you can " +
"authenticate your account for use with %(integrationsUrl)s. " +
"Do you wish to continue?",
"You are about to be taken to a third-party site so you can authenticate your account for use with %(integrationsUrl)s. Do you wish to continue?",
{ integrationsUrl: integrationsUrl },
)}
</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(
"Your messages are secured and only you and the recipient have " +
"the unique keys to unlock them.",
"Your messages are secured and only you and the recipient have the unique keys to unlock them.",
)}
</p>
</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(
"In encrypted rooms, your messages are secured and only you and the recipient have " +
"the unique keys to unlock them.",
"In encrypted rooms, your messages are secured and only you and the recipient have the unique keys to unlock them.",
)}
</p>
</div>

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -20,7 +20,7 @@ import React from "react";
import classNames from "classnames";
import AccessibleButton from "../elements/AccessibleButton";
import { _t, _td } from "../../../languageHandler";
import { _t, _td, TranslationKey } from "../../../languageHandler";
import E2EIcon, { E2EState } from "./E2EIcon";
import BaseAvatar from "../avatars/BaseAvatar";
import PresenceLabel from "./PresenceLabel";
@ -30,7 +30,7 @@ export enum PowerStatus {
Moderator = "moderator",
}
const PowerLabel: Record<PowerStatus, string> = {
const PowerLabel: Record<PowerStatus, TranslationKey> = {
[PowerStatus.Admin]: _td("Admin"),
[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 RoomContext from "../../../contexts/RoomContext";
import DMRoomMap from "../../../utils/DMRoomMap";
import { _t, _td } from "../../../languageHandler";
import { _t, _td, TranslationKey } from "../../../languageHandler";
import AccessibleButton, { ButtonEvent } from "../elements/AccessibleButton";
import MiniAvatarUploader, { AVATAR_SIZE } from "../elements/MiniAvatarUploader";
import RoomAvatar from "../avatars/RoomAvatar";
@ -44,7 +44,7 @@ function hasExpectedEncryptionSettings(matrixClient: MatrixClient, room: Room):
return isPublic || !privateShouldBeEncrypted(matrixClient) || isEncrypted;
}
const determineIntroMessage = (room: Room, encryptedSingle3rdPartyInvite: boolean): string => {
const determineIntroMessage = (room: Room, encryptedSingle3rdPartyInvite: boolean): TranslationKey => {
if (room instanceof LocalRoom) {
return _td("Send your first message to invite <displayName/> to chat");
}
@ -270,9 +270,7 @@ const NewRoomIntro: React.FC = () => {
}
const subText = _t(
"Your private messages are normally encrypted, but this room isn't. " +
"Usually this is due to an unsupported device or method being used, " +
"like email invites.",
"Your private messages are normally encrypted, but this room isn't. Usually this is due to an unsupported device or method being used, like email invites.",
);
let subButton: JSX.Element | undefined;

View file

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

View file

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

View file

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

View file

@ -496,7 +496,10 @@ export class SendMessageComposer extends React.Component<ISendMessageComposerPro
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.
attachMentions(this.props.mxClient.getSafeUserId(), content, model, replyToEvent);
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 { IWidget } from "matrix-widget-api";
import { _t, _td } from "../../../languageHandler";
import { _t, _td, TranslationKey } from "../../../languageHandler";
import AppTile from "../elements/AppTile";
import { MatrixClientPeg } from "../../../MatrixClientPeg";
import dis from "../../../dispatcher/dispatcher";
@ -159,7 +159,7 @@ export default class Stickerpicker extends React.PureComponent<IProps, IState> {
this.sendVisibilityToWidget(this.props.isStickerPickerOpen);
}
private imError(errorMsg: string, e: Error): void {
private imError(errorMsg: TranslationKey, e: Error): void {
logger.error(errorMsg, e);
this.setState({
imError: _t(errorMsg),

View file

@ -108,8 +108,7 @@ export default class ThirdPartyMemberInfo extends React.Component<IProps, IState
Modal.createDialog(ErrorDialog, {
title: _t("Failed to revoke invite"),
description: _t(
"Could not revoke the invite. The server may be experiencing a temporary problem or " +
"you do not have sufficient permissions to revoke the invite.",
"Could not revoke the invite. The server may be experiencing a temporary problem or 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 = (
<SettingsSubsectionText data-testid="summarised-status">
{_t(
"Your account has a cross-signing identity in secret storage, " +
"but it is not yet trusted by this session.",
"Your account has a cross-signing identity in secret storage, but it is not yet trusted by this session.",
)}
</SettingsSubsectionText>
);

View file

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

View file

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

View file

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

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