Migrate more strings to translation keys (#11694)

This commit is contained in:
Michael Telatynski 2023-10-03 19:17:26 +01:00 committed by GitHub
parent 677854d318
commit e1cfde0c6e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
201 changed files with 21074 additions and 18552 deletions

View file

@ -324,13 +324,13 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
const dialogAesthetics = { const dialogAesthetics = {
[SSOAuthEntry.PHASE_PREAUTH]: { [SSOAuthEntry.PHASE_PREAUTH]: {
title: _t("auth|uia|sso_title"), title: _t("auth|uia|sso_title"),
body: _t("To continue, use Single Sign On to prove your identity."), body: _t("auth|uia|sso_preauth_body"),
continueText: _t("auth|sso"), continueText: _t("auth|sso"),
continueKind: "primary", continueKind: "primary",
}, },
[SSOAuthEntry.PHASE_POSTAUTH]: { [SSOAuthEntry.PHASE_POSTAUTH]: {
title: _t("Confirm encryption setup"), title: _t("encryption|confirm_encryption_setup_title"),
body: _t("Click the button below to confirm setting up encryption."), body: _t("encryption|confirm_encryption_setup_body"),
continueText: _t("action|confirm"), continueText: _t("action|confirm"),
continueKind: "primary", continueKind: "primary",
}, },

View file

@ -1228,7 +1228,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
const isSpace = roomToLeave?.isSpaceRoom(); const isSpace = roomToLeave?.isSpaceRoom();
Modal.createDialog(QuestionDialog, { Modal.createDialog(QuestionDialog, {
title: isSpace ? _t("Leave space") : _t("action|leave_room"), title: isSpace ? _t("space|leave_dialog_action") : _t("action|leave_room"),
description: ( description: (
<span> <span>
{isSpace {isSpace
@ -1271,7 +1271,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
if (room) RoomListStore.instance.manualRoomUpdate(room, RoomUpdateCause.RoomRemoved); if (room) RoomListStore.instance.manualRoomUpdate(room, RoomUpdateCause.RoomRemoved);
}) })
.catch((err) => { .catch((err) => {
const errCode = err.errcode || _td("unknown error code"); const errCode = err.errcode || _td("error|unknown_error_code");
Modal.createDialog(ErrorDialog, { Modal.createDialog(ErrorDialog, {
title: _t("error_dialog|forget_room_failed", { errCode }), title: _t("error_dialog|forget_room_failed", { errCode }),
description: err && err.message ? err.message : _t("invite|failed_generic"), description: err && err.message ? err.message : _t("invite|failed_generic"),

View file

@ -246,7 +246,7 @@ export default class RoomStatusBar extends React.PureComponent<IProps, IState> {
<> <>
<InlineSpinner w={20} h={20} /> <InlineSpinner w={20} h={20} />
{/* span for css */} {/* span for css */}
<span>{_t("Sending")}</span> <span>{_t("forward|sending")}</span>
</> </>
); );
} }

View file

@ -206,9 +206,9 @@ const Tile: React.FC<ITileProps> = ({
); );
} }
let description = _t("%(count)s members", { count: room.num_joined_members ?? 0 }); let description = _t("common|n_members", { count: room.num_joined_members ?? 0 });
if (numChildRooms !== undefined) { if (numChildRooms !== undefined) {
description += " · " + _t("%(count)s rooms", { count: numChildRooms }); description += " · " + _t("common|n_rooms", { count: numChildRooms });
} }
let topic: ReactNode | string | null; let topic: ReactNode | string | null;
@ -713,7 +713,7 @@ const ManageButtons: React.FC<IManageButtonsProps> = ({ hierarchy, selected, set
kind="danger_outline" kind="danger_outline"
disabled={disabled} disabled={disabled}
> >
{removing ? _t("Removing…") : _t("action|remove")} {removing ? _t("redact|ongoing") : _t("action|remove")}
</Button> </Button>
<Button <Button
{...props} {...props}
@ -857,7 +857,7 @@ const SpaceHierarchy: React.FC<IProps> = ({ space, initialText = "", showRoom, a
} else if (!hierarchy.canLoadMore) { } else if (!hierarchy.canLoadMore) {
results = ( results = (
<div className="mx_SpaceHierarchy_noResults"> <div className="mx_SpaceHierarchy_noResults">
<h3>{_t("No results found")}</h3> <h3>{_t("common|no_results_found")}</h3>
<div>{_t("space|no_search_result_hint")}</div> <div>{_t("space|no_search_result_hint")}</div>
</div> </div>
); );

View file

@ -407,7 +407,7 @@ const SpaceAddExistingRooms: React.FC<{
{_t("create_space|skip_action")} {_t("create_space|skip_action")}
</AccessibleButton> </AccessibleButton>
} }
filterPlaceholder={_t("Search for rooms")} filterPlaceholder={_t("space|room_filter_placeholder")}
onFinished={onFinished} onFinished={onFinished}
roomsRenderer={defaultRoomsRenderer} roomsRenderer={defaultRoomsRenderer}
dmsRenderer={defaultDmsRenderer} dmsRenderer={defaultDmsRenderer}
@ -508,7 +508,7 @@ const SpaceSetupPrivateInvite: React.FC<{
key={name} key={name}
name={name} name={name}
type="text" type="text"
label={_t("Email address")} label={_t("common|email_address")}
placeholder={_t("auth|email_field_label")} placeholder={_t("auth|email_field_label")}
value={emailAddresses[i]} value={emailAddresses[i]}
onChange={(ev: React.ChangeEvent<HTMLInputElement>) => setEmailAddress(i, ev.target.value)} onChange={(ev: React.ChangeEvent<HTMLInputElement>) => setEmailAddress(i, ev.target.value)}
@ -553,7 +553,7 @@ const SpaceSetupPrivateInvite: React.FC<{
} }
} catch (err) { } catch (err) {
logger.error("Failed to invite users to space: ", err); logger.error("Failed to invite users to space: ", err);
setError(_t("We couldn't invite those users. Please check the users you want to invite and try again.")); setError(_t("invite|error_invite"));
} }
setBusy(false); setBusy(false);
}; };

View file

@ -19,7 +19,7 @@ import classNames from "classnames";
import { logger } from "matrix-js-sdk/src/logger"; import { logger } from "matrix-js-sdk/src/logger";
import { SSOFlow, SSOAction } from "matrix-js-sdk/src/matrix"; import { SSOFlow, SSOAction } from "matrix-js-sdk/src/matrix";
import { _t, _td, UserFriendlyError } from "../../../languageHandler"; import { _t, UserFriendlyError } from "../../../languageHandler";
import Login, { ClientLoginFlow, OidcNativeFlow } from "../../../Login"; import Login, { ClientLoginFlow, OidcNativeFlow } from "../../../Login";
import { messageForConnectionError, messageForLoginError } from "../../../utils/ErrorUtils"; import { messageForConnectionError, messageForLoginError } from "../../../utils/ErrorUtils";
import AutoDiscoveryUtils from "../../../utils/AutoDiscoveryUtils"; import AutoDiscoveryUtils from "../../../utils/AutoDiscoveryUtils";
@ -41,16 +41,6 @@ import { filterBoolean } from "../../../utils/arrays";
import { Features } from "../../../settings/Settings"; import { Features } from "../../../settings/Settings";
import { startOidcLogin } from "../../../utils/oidc/authorize"; import { startOidcLogin } from "../../../utils/oidc/authorize";
// These are used in several places, and come from the js-sdk's autodiscovery
// stuff. We define them here so that they'll be picked up by i18n.
_td("Invalid homeserver discovery response");
_td("Failed to get autodiscovery configuration from server");
_td("Invalid base_url for m.homeserver");
_td("Homeserver URL does not appear to be a valid Matrix homeserver");
_td("Invalid identity server discovery response");
_td("Invalid base_url for m.identity_server");
_td("Identity server URL does not appear to be a valid identity server");
_td("General failure");
interface IProps { interface IProps {
serverConfig: ValidatedServerConfig; serverConfig: ValidatedServerConfig;
// If true, the component will consider itself busy. // If true, the component will consider itself busy.

View file

@ -204,7 +204,7 @@ export default class SetupEncryptionBody extends React.Component<IProps, IState>
{useRecoveryKeyButton} {useRecoveryKeyButton}
</div> </div>
<div className="mx_SetupEncryptionBody_reset"> <div className="mx_SetupEncryptionBody_reset">
{_t("Forgotten or lost all recovery methods? <a>Reset all</a>", undefined, { {_t("encryption|reset_all_button", undefined, {
a: (sub) => ( a: (sub) => (
<AccessibleButton <AccessibleButton
kind="link_inline" kind="link_inline"

View file

@ -315,7 +315,7 @@ export default class SoftLogout extends React.Component<IProps, IState> {
<p>{_t("auth|soft_logout_warning")}</p> <p>{_t("auth|soft_logout_warning")}</p>
<div> <div>
<AccessibleButton onClick={this.onClearAll} kind="danger"> <AccessibleButton onClick={this.onClearAll} kind="danger">
{_t("Clear all data")} {_t("auth|soft_logout|clear_data_button")}
</AccessibleButton> </AccessibleButton>
</div> </div>
</AuthBody> </AuthBody>

View file

@ -72,7 +72,7 @@ export const EnterEmail: React.FC<EnterEmailProps> = ({
<div className="mx_AuthBody_fieldRow"> <div className="mx_AuthBody_fieldRow">
<EmailField <EmailField
name="reset_email" // define a name so browser's password autofill gets less confused name="reset_email" // define a name so browser's password autofill gets less confused
label="Email address" label={_td("common|email_address")}
labelRequired={_td("auth|forgot_password_email_required")} labelRequired={_td("auth|forgot_password_email_required")}
labelInvalid={_td("auth|forgot_password_email_invalid")} labelInvalid={_td("auth|forgot_password_email_invalid")}
value={email} value={email}

View file

@ -94,7 +94,7 @@ export const VerifyEmailModal: React.FC<Props> = ({
<AccessibleButton <AccessibleButton
onClick={onCloseClick} onClick={onCloseClick}
className="mx_Dialog_cancelButton" className="mx_Dialog_cancelButton"
aria-label={_t("Close dialog")} aria-label={_t("dialog_close_label")}
/> />
</> </>
); );

View file

@ -407,7 +407,7 @@ export default class PasswordLogin extends React.PureComponent<IProps, IState> {
{_t("common|username")} {_t("common|username")}
</option> </option>
<option key={LoginField.Email} value={LoginField.Email}> <option key={LoginField.Email} value={LoginField.Email}>
{_t("Email address")} {_t("common|email_address")}
</option> </option>
<option key={LoginField.Password} value={LoginField.Password}> <option key={LoginField.Password} value={LoginField.Password}>
{_t("auth|msisdn_field_label")} {_t("auth|msisdn_field_label")}

View file

@ -387,7 +387,7 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
test: ({ value }, usernameAvailable) => test: ({ value }, usernameAvailable) =>
(!value || SAFE_LOCALPART_REGEX.test(value)) && (!value || SAFE_LOCALPART_REGEX.test(value)) &&
usernameAvailable !== UsernameAvailableStatus.Invalid, usernameAvailable !== UsernameAvailableStatus.Invalid,
invalid: () => _t("Some characters not allowed"), invalid: () => _t("room_settings|general|alias_field_safe_localpart_invalid"),
}, },
{ {
key: "available", key: "available",
@ -453,7 +453,7 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
} }
const emailLabel = this.authStepIsRequired("m.login.email.identity") const emailLabel = this.authStepIsRequired("m.login.email.identity")
? _td("auth|email_field_label") ? _td("auth|email_field_label")
: _td("Email (optional)"); : _td("auth|registration|continue_without_email_field_label");
return ( return (
<EmailField <EmailField
fieldRef={(field) => (this[RegistrationField.Email] = field)} fieldRef={(field) => (this[RegistrationField.Email] = field)}

View file

@ -71,7 +71,7 @@ const BeaconListItem: React.FC<Props & HTMLProps<HTMLLIElement>> = ({ beacon, ..
</div> </div>
</BeaconStatus> </BeaconStatus>
<span className="mx_BeaconListItem_lastUpdated"> <span className="mx_BeaconListItem_lastUpdated">
{_t("Updated %(humanizedUpdateTime)s", { humanizedUpdateTime })} {_t("location_sharing|live_update_time", { humanizedUpdateTime })}
</span> </span>
</div> </div>
</li> </li>

View file

@ -35,7 +35,7 @@ interface Props {
const BeaconExpiryTime: React.FC<{ beacon: Beacon }> = ({ beacon }) => { const BeaconExpiryTime: React.FC<{ beacon: Beacon }> = ({ beacon }) => {
const expiryTime = formatTime(new Date(getBeaconExpiryTimestamp(beacon))); const expiryTime = formatTime(new Date(getBeaconExpiryTimestamp(beacon)));
return <span className="mx_BeaconStatus_expiryTime">{_t("Live until %(expiryTime)s", { expiryTime })}</span>; return <span className="mx_BeaconStatus_expiryTime">{_t("location_sharing|live_until", { expiryTime })}</span>;
}; };
const BeaconStatus: React.FC<Props & HTMLProps<HTMLDivElement>> = ({ const BeaconStatus: React.FC<Props & HTMLProps<HTMLDivElement>> = ({
@ -61,13 +61,19 @@ const BeaconStatus: React.FC<Props & HTMLProps<HTMLDivElement>> = ({
)} )}
<div className="mx_BeaconStatus_description"> <div className="mx_BeaconStatus_description">
{displayStatus === BeaconDisplayStatus.Loading && ( {displayStatus === BeaconDisplayStatus.Loading && (
<span className="mx_BeaconStatus_description_status">{_t("Loading live location…")}</span> <span className="mx_BeaconStatus_description_status">
{_t("location_sharing|loading_live_location")}
</span>
)} )}
{displayStatus === BeaconDisplayStatus.Stopped && ( {displayStatus === BeaconDisplayStatus.Stopped && (
<span className="mx_BeaconStatus_description_status">{_t("Live location ended")}</span> <span className="mx_BeaconStatus_description_status">
{_t("location_sharing|live_location_ended")}
</span>
)} )}
{displayStatus === BeaconDisplayStatus.Error && ( {displayStatus === BeaconDisplayStatus.Error && (
<span className="mx_BeaconStatus_description_status">{_t("Live location error")}</span> <span className="mx_BeaconStatus_description_status">
{_t("location_sharing|live_location_error")}
</span>
)} )}
{displayStatus === BeaconDisplayStatus.Active && beacon && ( {displayStatus === BeaconDisplayStatus.Active && beacon && (
<> <>

View file

@ -160,7 +160,9 @@ const BeaconViewDialog: React.FC<IProps> = ({ initialFocusedBeacon, roomId, matr
)} )}
{!centerGeoUri && !mapDisplayError && ( {!centerGeoUri && !mapDisplayError && (
<MapFallback data-testid="beacon-view-dialog-map-fallback" className="mx_BeaconViewDialog_map"> <MapFallback data-testid="beacon-view-dialog-map-fallback" className="mx_BeaconViewDialog_map">
<span className="mx_BeaconViewDialog_mapFallbackMessage">{_t("No live locations")}</span> <span className="mx_BeaconViewDialog_mapFallbackMessage">
{_t("location_sharing|live_locations_empty")}
</span>
<AccessibleButton <AccessibleButton
kind="primary" kind="primary"
onClick={onFinished} onClick={onFinished}
@ -185,7 +187,7 @@ const BeaconViewDialog: React.FC<IProps> = ({ initialFocusedBeacon, roomId, matr
> >
<LiveLocationIcon height={12} /> <LiveLocationIcon height={12} />
&nbsp; &nbsp;
{_t("View list")} {_t("action|view_list")}
</AccessibleButton> </AccessibleButton>
)} )}
<DialogOwnBeaconStatus roomId={roomId} /> <DialogOwnBeaconStatus roomId={roomId} />

View file

@ -33,11 +33,11 @@ const DialogSidebar: React.FC<Props> = ({ beacons, onBeaconClick, requestClose }
return ( return (
<div className="mx_DialogSidebar"> <div className="mx_DialogSidebar">
<div className="mx_DialogSidebar_header"> <div className="mx_DialogSidebar_header">
<Heading size="4">{_t("View List")}</Heading> <Heading size="4">{_t("action|view_list")}</Heading>
<AccessibleButton <AccessibleButton
className="mx_DialogSidebar_closeButton" className="mx_DialogSidebar_closeButton"
onClick={requestClose} onClick={requestClose}
title={_t("Close sidebar")} title={_t("location_sharing|close_sidebar")}
data-testid="dialog-sidebar-close" data-testid="dialog-sidebar-close"
> >
<CloseIcon className="mx_DialogSidebar_closeButtonIcon" /> <CloseIcon className="mx_DialogSidebar_closeButtonIcon" />
@ -50,7 +50,7 @@ const DialogSidebar: React.FC<Props> = ({ beacons, onBeaconClick, requestClose }
))} ))}
</ol> </ol>
) : ( ) : (
<div className="mx_DialogSidebar_noResults">{_t("No live locations")}</div> <div className="mx_DialogSidebar_noResults">{_t("location_sharing|live_locations_empty")}</div>
)} )}
</div> </div>
); );

View file

@ -52,12 +52,12 @@ const chooseBestBeacon = (
const getLabel = (hasStoppingErrors: boolean, hasLocationErrors: boolean): string => { const getLabel = (hasStoppingErrors: boolean, hasLocationErrors: boolean): string => {
if (hasStoppingErrors) { if (hasStoppingErrors) {
return _t("An error occurred while stopping your live location"); return _t("location_sharing|error_stopping_live_location");
} }
if (hasLocationErrors) { if (hasLocationErrors) {
return _t("An error occurred whilst sharing your live location"); return _t("location_sharing|error_sharing_live_location");
} }
return _t("You are sharing your live location"); return _t("location_sharing|live_location_active");
}; };
const useLivenessMonitor = (liveBeaconIds: BeaconIdentifier[], beacons: Map<BeaconIdentifier, Beacon>): void => { const useLivenessMonitor = (liveBeaconIds: BeaconIdentifier[], beacons: Map<BeaconIdentifier, Beacon>): void => {

View file

@ -51,7 +51,7 @@ const OwnBeaconStatus: React.FC<Props & HTMLProps<HTMLDivElement>> = ({ beacon,
<BeaconStatus <BeaconStatus
beacon={beacon} beacon={beacon}
displayStatus={ownDisplayStatus} displayStatus={ownDisplayStatus}
label={_t("Live location enabled")} label={_t("location_sharing|live_location_enabled")}
displayLiveTimeRemaining displayLiveTimeRemaining
{...rest} {...rest}
> >

View file

@ -32,12 +32,12 @@ import { Action } from "../../../dispatcher/actions";
const getLabel = (hasLocationPublishError: boolean, hasStopSharingError: boolean): string => { const getLabel = (hasLocationPublishError: boolean, hasStopSharingError: boolean): string => {
if (hasLocationPublishError) { if (hasLocationPublishError) {
return _t("An error occurred whilst sharing your live location, please try again"); return _t("location_sharing|error_sharing_live_location_try_again");
} }
if (hasStopSharingError) { if (hasStopSharingError) {
return _t("An error occurred while stopping your live location, please try again"); return _t("location_sharing|error_stopping_live_location_try_again");
} }
return _t("You are sharing your live location"); return _t("location_sharing|live_location_active");
}; };
interface RoomLiveShareWarningInnerProps { interface RoomLiveShareWarningInnerProps {

View file

@ -46,7 +46,7 @@ const ShareLatestLocation: React.FC<Props> = ({ latestLocationState }) => {
return ( return (
<> <>
<TooltipTarget label={_t("Open in OpenStreetMap")}> <TooltipTarget label={_t("timeline|context_menu|open_in_osm")}>
<a data-testid="open-location-in-osm" href={mapLink} target="_blank" rel="noreferrer noopener"> <a data-testid="open-location-in-osm" href={mapLink} target="_blank" rel="noreferrer noopener">
<ExternalLinkIcon className="mx_ShareLatestLocation_icon" /> <ExternalLinkIcon className="mx_ShareLatestLocation_icon" />
</a> </a>

View file

@ -22,9 +22,9 @@ import { IProps as IContextMenuProps } from "../../structures/ContextMenu";
import { _t, _td, TranslationKey } from "../../../languageHandler"; import { _t, _td, TranslationKey } from "../../../languageHandler";
const SECTION_NAMES: Record<MediaDeviceKindEnum, TranslationKey> = { const SECTION_NAMES: Record<MediaDeviceKindEnum, TranslationKey> = {
[MediaDeviceKindEnum.AudioInput]: _td("Input devices"), [MediaDeviceKindEnum.AudioInput]: _td("voip|input_devices"),
[MediaDeviceKindEnum.AudioOutput]: _td("Output devices"), [MediaDeviceKindEnum.AudioOutput]: _td("voip|output_devices"),
[MediaDeviceKindEnum.VideoInput]: _td("Cameras"), [MediaDeviceKindEnum.VideoInput]: _td("common|cameras"),
}; };
interface IDeviceContextMenuDeviceProps { interface IDeviceContextMenuDeviceProps {

View file

@ -47,14 +47,14 @@ export default class LegacyCallContextMenu extends React.Component<IProps> {
}; };
public render(): React.ReactNode { public render(): React.ReactNode {
const holdUnholdCaption = this.props.call.isRemoteOnHold() ? _t("Resume") : _t("Hold"); const holdUnholdCaption = this.props.call.isRemoteOnHold() ? _t("action|resume") : _t("action|hold");
const handler = this.props.call.isRemoteOnHold() ? this.onUnholdClick : this.onHoldClick; const handler = this.props.call.isRemoteOnHold() ? this.onUnholdClick : this.onHoldClick;
let transferItem; let transferItem;
if (this.props.call.opponentCanBeTransferred()) { if (this.props.call.opponentCanBeTransferred()) {
transferItem = ( transferItem = (
<MenuItem className="mx_LegacyCallContextMenu_item" onClick={this.onTransferClick}> <MenuItem className="mx_LegacyCallContextMenu_item" onClick={this.onTransferClick}>
{_t("Transfer")} {_t("action|transfer")}
</MenuItem> </MenuItem>
); );
} }

View file

@ -414,7 +414,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
resendReactionsButton = ( resendReactionsButton = (
<IconizedContextMenuOption <IconizedContextMenuOption
iconClassName="mx_MessageContextMenu_iconResend" iconClassName="mx_MessageContextMenu_iconResend"
label={_t("Resend %(unsentCount)s reaction(s)", { unsentCount: unsentReactionsCount })} label={_t("timeline|context_menu|resent_unsent_reactions", { unsentCount: unsentReactionsCount })}
onClick={this.onResendReactionsClick} onClick={this.onResendReactionsClick}
/> />
); );
@ -439,7 +439,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
<IconizedContextMenuOption <IconizedContextMenuOption
iconClassName="mx_MessageContextMenu_iconOpenInMapSite" iconClassName="mx_MessageContextMenu_iconOpenInMapSite"
onClick={null} onClick={null}
label={_t("Open in OpenStreetMap")} label={_t("timeline|context_menu|open_in_osm")}
element="a" element="a"
{...{ {...{
href: mapSiteLink, href: mapSiteLink,
@ -518,7 +518,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
endPollButton = ( endPollButton = (
<IconizedContextMenuOption <IconizedContextMenuOption
iconClassName="mx_MessageContextMenu_iconEndPoll" iconClassName="mx_MessageContextMenu_iconEndPoll"
label={_t("End Poll")} label={_t("poll|end_title")}
onClick={this.onEndPollClick} onClick={this.onEndPollClick}
/> />
); );

View file

@ -108,7 +108,7 @@ const SpaceContextMenu: React.FC<IProps> = ({ space, hideHeader, onFinished, ...
data-testid="leave-option" data-testid="leave-option"
iconClassName="mx_SpacePanel_iconLeave" iconClassName="mx_SpacePanel_iconLeave"
className="mx_IconizedContextMenu_option_red" className="mx_IconizedContextMenu_option_red"
label={_t("Leave space")} label={_t("space|leave_dialog_action")}
onClick={onLeaveClick} onClick={onLeaveClick}
/> />
); );

View file

@ -92,7 +92,7 @@ const ThreadListContextMenu: React.FC<ThreadListContextMenuProps> = ({
{...props} {...props}
className="mx_BaseCard_header_title_button--option" className="mx_BaseCard_header_title_button--option"
onClick={openMenu} onClick={openMenu}
title={_t("Thread options")} title={_t("right_panel|thread_list|context_menu_label")}
isExpanded={menuDisplayed} isExpanded={menuDisplayed}
inputRef={button} inputRef={button}
data-testid="threadlist-dropdown-button" data-testid="threadlist-dropdown-button"

View file

@ -135,9 +135,10 @@ export const WidgetContextMenu: React.FC<IProps> = ({
} catch (err) { } catch (err) {
logger.error("Failed to start livestream", err); logger.error("Failed to start livestream", err);
// XXX: won't i18n well, but looks like widget api only support 'message'? // XXX: won't i18n well, but looks like widget api only support 'message'?
const message = err instanceof Error ? err.message : _t("Unable to start audio streaming."); const message =
err instanceof Error ? err.message : _t("widget|error_unable_start_audio_stream_description");
Modal.createDialog(ErrorDialog, { Modal.createDialog(ErrorDialog, {
title: _t("Failed to start livestream"), title: _t("widget|error_unable_start_audio_stream_title"),
description: message, description: message,
}); });
} }

View file

@ -36,7 +36,7 @@ const AddExistingSubspaceDialog: React.FC<IProps> = ({ space, onCreateSubspaceCl
<BaseDialog <BaseDialog
title={ title={
<SubspaceSelector <SubspaceSelector
title={_t("Add existing space")} title={_t("space|add_existing_subspace|space_dropdown_title")}
space={space} space={space}
value={selectedSpace} value={selectedSpace}
onChange={setSelectedSpace} onChange={setSelectedSpace}
@ -53,13 +53,13 @@ const AddExistingSubspaceDialog: React.FC<IProps> = ({ space, onCreateSubspaceCl
onFinished={onFinished} onFinished={onFinished}
footerPrompt={ footerPrompt={
<> <>
<div>{_t("Want to add a new space instead?")}</div> <div>{_t("space|add_existing_subspace|create_prompt")}</div>
<AccessibleButton onClick={onCreateSubspaceClick} kind="link"> <AccessibleButton onClick={onCreateSubspaceClick} kind="link">
{_t("Create a new space")} {_t("space|add_existing_subspace|create_button")}
</AccessibleButton> </AccessibleButton>
</> </>
} }
filterPlaceholder={_t("Search for spaces")} filterPlaceholder={_t("space|add_existing_subspace|filter_placeholder")}
spacesRenderer={defaultSpacesRenderer} spacesRenderer={defaultSpacesRenderer}
/> />
</MatrixClientContext.Provider> </MatrixClientContext.Provider>

View file

@ -241,7 +241,9 @@ export const AddExistingToSpace: React.FC<IAddExistingToSpaceProps> = ({
/> />
<span className="mx_AddExistingToSpaceDialog_error"> <span className="mx_AddExistingToSpaceDialog_error">
<div className="mx_AddExistingToSpaceDialog_errorHeading">{_t("Not all selected were added")}</div> <div className="mx_AddExistingToSpaceDialog_errorHeading">
{_t("space|add_existing_room_space|error_heading")}
</div>
<div className="mx_AddExistingToSpaceDialog_errorCaption">{_t("action|try_again")}</div> <div className="mx_AddExistingToSpaceDialog_errorCaption">{_t("action|try_again")}</div>
</span> </span>
@ -255,7 +257,7 @@ export const AddExistingToSpace: React.FC<IAddExistingToSpaceProps> = ({
<span> <span>
<ProgressBar value={progress} max={selectedToAdd.size} /> <ProgressBar value={progress} max={selectedToAdd.size} />
<div className="mx_AddExistingToSpaceDialog_progressText"> <div className="mx_AddExistingToSpaceDialog_progressText">
{_t("Adding rooms... (%(progress)s out of %(count)s)", { {_t("space|add_existing_room_space|progress_text", {
count: selectedToAdd.size, count: selectedToAdd.size,
progress, progress,
})} })}
@ -389,7 +391,7 @@ const defaultRendererFactory =
export const defaultRoomsRenderer = defaultRendererFactory(_td("common|rooms")); export const defaultRoomsRenderer = defaultRendererFactory(_td("common|rooms"));
export const defaultSpacesRenderer = defaultRendererFactory(_td("common|spaces")); export const defaultSpacesRenderer = defaultRendererFactory(_td("common|spaces"));
export const defaultDmsRenderer = defaultRendererFactory(_td("Direct Messages")); export const defaultDmsRenderer = defaultRendererFactory(_td("space|add_existing_room_space|dm_heading"));
interface ISubspaceSelectorProps { interface ISubspaceSelectorProps {
title: string; title: string;
@ -418,7 +420,7 @@ export const SubspaceSelector: React.FC<ISubspaceSelectorProps> = ({ title, spac
onChange(options.find((space) => space.roomId === key) || space); onChange(options.find((space) => space.roomId === key) || space);
}} }}
value={value.roomId} value={value.roomId}
label={_t("Space selection")} label={_t("space|add_existing_room_space|space_dropdown_label")}
> >
{ {
options.map((space) => { options.map((space) => {
@ -461,7 +463,7 @@ const AddExistingToSpaceDialog: React.FC<IProps> = ({ space, onCreateRoomClick,
<BaseDialog <BaseDialog
title={ title={
<SubspaceSelector <SubspaceSelector
title={_t("Add existing rooms")} title={_t("space|add_existing_room_space|space_dropdown_title")}
space={space} space={space}
value={selectedSpace} value={selectedSpace}
onChange={setSelectedSpace} onChange={setSelectedSpace}
@ -478,7 +480,7 @@ const AddExistingToSpaceDialog: React.FC<IProps> = ({ space, onCreateRoomClick,
onFinished={onFinished} onFinished={onFinished}
footerPrompt={ footerPrompt={
<> <>
<div>{_t("Want to add a new room instead?")}</div> <div>{_t("space|add_existing_room_space|create")}</div>
<AccessibleButton <AccessibleButton
kind="link" kind="link"
onClick={(ev: ButtonEvent) => { onClick={(ev: ButtonEvent) => {
@ -486,11 +488,11 @@ const AddExistingToSpaceDialog: React.FC<IProps> = ({ space, onCreateRoomClick,
onFinished(); onFinished();
}} }}
> >
{_t("Create a new room")} {_t("space|add_existing_room_space|create_prompt")}
</AccessibleButton> </AccessibleButton>
</> </>
} }
filterPlaceholder={_t("Search for rooms")} filterPlaceholder={_t("space|room_filter_placeholder")}
roomsRenderer={defaultRoomsRenderer} roomsRenderer={defaultRoomsRenderer}
spacesRenderer={() => ( spacesRenderer={() => (
<div className="mx_AddExistingToSpace_section"> <div className="mx_AddExistingToSpace_section">
@ -502,7 +504,7 @@ const AddExistingToSpaceDialog: React.FC<IProps> = ({ space, onCreateRoomClick,
onFinished(); onFinished();
}} }}
> >
{_t("Adding spaces has moved.")} {_t("space|add_existing_room_space|subspace_moved_note")}
</AccessibleButton> </AccessibleButton>
</div> </div>
)} )}

View file

@ -70,15 +70,13 @@ export default function AskInviteAnywayDialog({
</li> </li>
)); ));
const description = const description = descriptionProp ?? _t("invite|unable_find_profiles_description_default");
descriptionProp ??
_t("Unable to find profiles for the Matrix IDs listed below - would you like to invite them anyway?");
return ( return (
<BaseDialog <BaseDialog
className="mx_RetryInvitesDialog" className="mx_RetryInvitesDialog"
onFinished={onGiveUpClicked} onFinished={onGiveUpClicked}
title={_t("The following users may not exist")} title={_t("invite|unable_find_profiles_title")}
contentId="mx_Dialog_content" contentId="mx_Dialog_content"
> >
<div id="mx_Dialog_content"> <div id="mx_Dialog_content">
@ -89,10 +87,10 @@ export default function AskInviteAnywayDialog({
<div className="mx_Dialog_buttons"> <div className="mx_Dialog_buttons">
<button onClick={onGiveUpClicked}>{_t("action|close")}</button> <button onClick={onGiveUpClicked}>{_t("action|close")}</button>
<button onClick={onInviteNeverWarnClicked}> <button onClick={onInviteNeverWarnClicked}>
{inviteNeverWarnLabel ?? _t("Invite anyway and never warn me again")} {inviteNeverWarnLabel ?? _t("invite|unable_find_profiles_invite_never_warn_label_default")}
</button> </button>
<button onClick={onInviteClicked} autoFocus={true}> <button onClick={onInviteClicked} autoFocus={true}>
{inviteLabel ?? _t("Invite anyway")} {inviteLabel ?? _t("invite|unable_find_profiles_invite_label_default")}
</button> </button>
</div> </div>
</BaseDialog> </BaseDialog>

View file

@ -127,7 +127,7 @@ export default class BaseDialog extends React.Component<IProps> {
<AccessibleButton <AccessibleButton
onClick={this.onCancelClick} onClick={this.onCancelClick}
className="mx_Dialog_cancelButton" className="mx_Dialog_cancelButton"
aria-label={_t("Close dialog")} aria-label={_t("dialog_close_label")}
/> />
); );
} }

View file

@ -37,7 +37,7 @@ const BetaFeedbackDialog: React.FC<IProps> = ({ featureId, onFinished }) => {
return ( return (
<GenericFeatureFeedbackDialog <GenericFeatureFeedbackDialog
title={_t("%(featureName)s Beta feedback", { featureName: info.title })} title={_t("labs|beta_feedback_title", { featureName: info.title })}
subheading={info.feedbackSubheading ? _t(info.feedbackSubheading) : undefined} subheading={info.feedbackSubheading ? _t(info.feedbackSubheading) : undefined}
onFinished={onFinished} onFinished={onFinished}
rageshakeLabel={info.feedbackLabel} rageshakeLabel={info.feedbackLabel}
@ -57,7 +57,7 @@ const BetaFeedbackDialog: React.FC<IProps> = ({ featureId, onFinished }) => {
}); });
}} }}
> >
{_t("To leave the beta, visit your settings.")} {_t("labs|beta_feedback_leave_button")}
</AccessibleButton> </AccessibleButton>
</GenericFeatureFeedbackDialog> </GenericFeatureFeedbackDialog>
); );

View file

@ -98,7 +98,7 @@ export default class BugReportDialog extends React.Component<IProps, IState> {
private onSubmit = (): void => { private onSubmit = (): void => {
if ((!this.state.text || !this.state.text.trim()) && (!this.state.issueUrl || !this.state.issueUrl.trim())) { if ((!this.state.text || !this.state.text.trim()) && (!this.state.issueUrl || !this.state.issueUrl.trim())) {
this.setState({ this.setState({
err: _t("Please tell us what went wrong or, better, create a GitHub issue that describes the problem."), err: _t("bug_reporting|error_empty"),
}); });
return; return;
} }
@ -109,7 +109,7 @@ export default class BugReportDialog extends React.Component<IProps, IState> {
(this.state.issueUrl.length > 0 ? this.state.issueUrl : "No issue link given"); (this.state.issueUrl.length > 0 ? this.state.issueUrl : "No issue link given");
this.setState({ busy: true, progress: null, err: null }); this.setState({ busy: true, progress: null, err: null });
this.sendProgressCallback(_t("Preparing to send logs")); this.sendProgressCallback(_t("bug_reporting|preparing_logs"));
sendBugReport(SdkConfig.get().bug_report_endpoint_url, { sendBugReport(SdkConfig.get().bug_report_endpoint_url, {
userText, userText,
@ -121,8 +121,8 @@ export default class BugReportDialog extends React.Component<IProps, IState> {
if (!this.unmounted) { if (!this.unmounted) {
this.props.onFinished(false); this.props.onFinished(false);
Modal.createDialog(QuestionDialog, { Modal.createDialog(QuestionDialog, {
title: _t("Logs sent"), title: _t("bug_reporting|logs_sent"),
description: _t("Thank you!"), description: _t("bug_reporting|thank_you"),
hasCancelButton: false, hasCancelButton: false,
}); });
} }
@ -132,7 +132,7 @@ export default class BugReportDialog extends React.Component<IProps, IState> {
this.setState({ this.setState({
busy: false, busy: false,
progress: null, progress: null,
err: _t("Failed to send logs: ") + `${err.message}`, err: _t("bug_reporting|failed_send_logs") + `${err.message}`,
}); });
} }
}, },
@ -143,7 +143,7 @@ export default class BugReportDialog extends React.Component<IProps, IState> {
private onDownload = async (): Promise<void> => { private onDownload = async (): Promise<void> => {
this.setState({ downloadBusy: true }); this.setState({ downloadBusy: true });
this.downloadProgressCallback(_t("Preparing to download logs")); this.downloadProgressCallback(_t("bug_reporting|preparing_download"));
try { try {
await downloadBugReport({ await downloadBugReport({
@ -160,7 +160,8 @@ export default class BugReportDialog extends React.Component<IProps, IState> {
if (!this.unmounted) { if (!this.unmounted) {
this.setState({ this.setState({
downloadBusy: false, downloadBusy: false,
downloadProgress: _t("Failed to send logs: ") + `${err instanceof Error ? err.message : ""}`, downloadProgress:
_t("bug_reporting|failed_send_logs") + `${err instanceof Error ? err.message : ""}`,
}); });
} }
} }
@ -208,7 +209,7 @@ export default class BugReportDialog extends React.Component<IProps, IState> {
if (window.Modernizr && Object.values(window.Modernizr).some((support) => support === false)) { if (window.Modernizr && Object.values(window.Modernizr).some((support) => support === false)) {
warning = ( warning = (
<p> <p>
<b>{_t("Reminder: Your browser is unsupported, so your experience may be unpredictable.")}</b> <b>{_t("bug_reporting|unsupported_browser")}</b>
</p> </p>
); );
} }
@ -261,7 +262,7 @@ export default class BugReportDialog extends React.Component<IProps, IState> {
<Field <Field
className="mx_BugReportDialog_field_input" className="mx_BugReportDialog_field_input"
element="textarea" element="textarea"
label={_t("Notes")} label={_t("bug_reporting|textarea_label")}
rows={5} rows={5}
onChange={this.onTextChange} onChange={this.onTextChange}
value={this.state.text} value={this.state.text}

View file

@ -62,10 +62,10 @@ const BulkRedactDialog: React.FC<Props> = (props) => {
return ( return (
<InfoDialog <InfoDialog
onFinished={onFinished} onFinished={onFinished}
title={_t("No recent messages by %(user)s found", { user: member.name })} title={_t("user_info|redact|no_recent_messages_title", { user: member.name })}
description={ description={
<div> <div>
<p>{_t("Try scrolling up in the timeline to see if there are any earlier ones.")}</p> <p>{_t("user_info|redact|no_recent_messages_description")}</p>
</div> </div>
} }
/> />
@ -108,32 +108,21 @@ const BulkRedactDialog: React.FC<Props> = (props) => {
<BaseDialog <BaseDialog
className="mx_BulkRedactDialog" className="mx_BulkRedactDialog"
onFinished={onFinished} onFinished={onFinished}
title={_t("Remove recent messages by %(user)s", { user })} title={_t("user_info|redact|confirm_title", { user })}
contentId="mx_Dialog_content" contentId="mx_Dialog_content"
> >
<div className="mx_Dialog_content" id="mx_Dialog_content"> <div className="mx_Dialog_content" id="mx_Dialog_content">
<p> <p>{_t("user_info|redact|confirm_description_1", { count, user })}</p>
{_t( <p>{_t("user_info|redact|confirm_description_2")}</p>
"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.",
)}
</p>
<StyledCheckbox checked={keepStateEvents} onChange={(e) => setKeepStateEvents(e.target.checked)}> <StyledCheckbox checked={keepStateEvents} onChange={(e) => setKeepStateEvents(e.target.checked)}>
{_t("Preserve system messages")} {_t("user_info|redact|confirm_keep_state_label")}
</StyledCheckbox> </StyledCheckbox>
<div className="mx_BulkRedactDialog_checkboxMicrocopy"> <div className="mx_BulkRedactDialog_checkboxMicrocopy">
{_t( {_t("user_info|redact|confirm_keep_state_explainer")}
"Uncheck if you also want to remove system messages on this user (e.g. membership change, profile change…)",
)}
</div> </div>
</div> </div>
<DialogButtons <DialogButtons
primaryButton={_t("Remove %(count)s messages", { count })} primaryButton={_t("user_info|redact|confirm_button", { count })}
primaryButtonClass="danger" primaryButtonClass="danger"
primaryDisabled={count === 0} primaryDisabled={count === 0}
onPrimaryButtonClick={() => { onPrimaryButtonClick={() => {

View file

@ -22,14 +22,8 @@ import InfoDialog from "./InfoDialog";
export const createCantStartVoiceMessageBroadcastDialog = (): void => { export const createCantStartVoiceMessageBroadcastDialog = (): void => {
Modal.createDialog(InfoDialog, { Modal.createDialog(InfoDialog, {
title: _t("Can't start voice message"), title: _t("voice_message|cant_start_broadcast_title"),
description: ( description: <p>{_t("voice_message|cant_start_broadcast_description")}</p>,
<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.",
)}
</p>
),
hasCloseButton: true, hasCloseButton: true,
}); });
}; };

View file

@ -93,7 +93,7 @@ export default class ChangelogDialog extends React.Component<IProps, State> {
if (this.state[repo] == null) { if (this.state[repo] == null) {
content = <Spinner key={repo} />; content = <Spinner key={repo} />;
} else if (typeof this.state[repo] === "string") { } else if (typeof this.state[repo] === "string") {
content = _t("Unable to load commit detail: %(msg)s", { content = _t("update|error_unable_load_commit", {
msg: this.state[repo], msg: this.state[repo],
}); });
} else { } else {
@ -111,13 +111,17 @@ export default class ChangelogDialog extends React.Component<IProps, State> {
const content = ( const content = (
<div className="mx_ChangelogDialog_content"> <div className="mx_ChangelogDialog_content">
{this.props.version == null || this.props.newVersion == null ? <h2>{_t("Unavailable")}</h2> : logs} {this.props.version == null || this.props.newVersion == null ? (
<h2>{_t("update|unavailable")}</h2>
) : (
logs
)}
</div> </div>
); );
return ( return (
<QuestionDialog <QuestionDialog
title={_t("Changelog")} title={_t("update|changelog")}
description={content} description={content}
button={_t("action|update")} button={_t("action|update")}
onFinished={this.props.onFinished} onFinished={this.props.onFinished}

View file

@ -88,12 +88,12 @@ export default class ConfirmAndWaitRedactDialog extends React.PureComponent<IPro
<ErrorDialog <ErrorDialog
onFinished={this.props.onFinished} onFinished={this.props.onFinished}
title={_t("common|error")} title={_t("common|error")}
description={_t("You cannot delete this message. (%(code)s)", { code })} description={_t("redact|error", { code })}
/> />
); );
} else { } else {
return ( return (
<BaseDialog onFinished={this.props.onFinished} hasCancel={false} title={_t("Removing…")}> <BaseDialog onFinished={this.props.onFinished} hasCancel={false} title={_t("redact|ongoing")}>
<Spinner /> <Spinner />
</BaseDialog> </BaseDialog>
); );

View file

@ -36,17 +36,17 @@ interface IProps {
*/ */
export default class ConfirmRedactDialog extends React.Component<IProps> { export default class ConfirmRedactDialog extends React.Component<IProps> {
public render(): React.ReactNode { public render(): React.ReactNode {
let description = _t("Are you sure you wish to remove (delete) this event?"); let description = _t("redact|confirm_description");
if (this.props.event.isState()) { if (this.props.event.isState()) {
description += " " + _t("Note that removing room changes like this could undo the change."); description += " " + _t("redact|confirm_description_state");
} }
return ( return (
<TextInputDialog <TextInputDialog
onFinished={this.props.onFinished} onFinished={this.props.onFinished}
title={_t("Confirm Removal")} title={_t("redact|confirm_button")}
description={description} description={description}
placeholder={_t("Reason (optional)")} placeholder={_t("redact|reason_label")}
focus focus
button={_t("action|remove")} button={_t("action|remove")}
/> />
@ -105,7 +105,7 @@ export function createRedactEventDialog({
// display error message stating you couldn't delete this. // display error message stating you couldn't delete this.
Modal.createDialog(ErrorDialog, { Modal.createDialog(ErrorDialog, {
title: _t("common|error"), title: _t("common|error"),
description: _t("You cannot delete this message. (%(code)s)", { code }), description: _t("redact|error", { code }),
}); });
} }
} }

View file

@ -39,17 +39,13 @@ export default class ConfirmWipeDeviceDialog extends React.Component<IProps> {
className="mx_ConfirmWipeDeviceDialog" className="mx_ConfirmWipeDeviceDialog"
hasCancel={true} hasCancel={true}
onFinished={this.props.onFinished} onFinished={this.props.onFinished}
title={_t("Clear all data in this session?")} title={_t("auth|soft_logout|clear_data_title")}
> >
<div className="mx_ConfirmWipeDeviceDialog_content"> <div className="mx_ConfirmWipeDeviceDialog_content">
<p> <p>{_t("auth|soft_logout|clear_data_description")}</p>
{_t(
"Clearing all data from this session is permanent. Encrypted messages will be lost unless their keys have been backed up.",
)}
</p>
</div> </div>
<DialogButtons <DialogButtons
primaryButton={_t("Clear all data")} primaryButton={_t("auth|soft_logout|clear_data_button")}
onPrimaryButtonClick={this.onConfirm} onPrimaryButtonClick={this.onConfirm}
primaryButtonClass="danger" primaryButtonClass="danger"
cancelButton={_t("action|cancel")} cancelButton={_t("action|cancel")}

View file

@ -100,7 +100,7 @@ const CreateSubspaceDialog: React.FC<IProps> = ({ space, onAddExistingSpaceClick
joinRuleMicrocopy = ( joinRuleMicrocopy = (
<p> <p>
{_t( {_t(
"Anyone in <SpaceName/> will be able to find and join.", "create_space|subspace_join_rule_restricted_description",
{}, {},
{ {
SpaceName: () => <b>{parentSpace.name}</b>, SpaceName: () => <b>{parentSpace.name}</b>,
@ -112,7 +112,7 @@ const CreateSubspaceDialog: React.FC<IProps> = ({ space, onAddExistingSpaceClick
joinRuleMicrocopy = ( joinRuleMicrocopy = (
<p> <p>
{_t( {_t(
"Anyone will be able to find and join this space, not just members of <SpaceName/>.", "create_space|subspace_join_rule_public_description",
{}, {},
{ {
SpaceName: () => <b>{parentSpace.name}</b>, SpaceName: () => <b>{parentSpace.name}</b>,
@ -121,14 +121,14 @@ const CreateSubspaceDialog: React.FC<IProps> = ({ space, onAddExistingSpaceClick
</p> </p>
); );
} else if (joinRule === JoinRule.Invite) { } else if (joinRule === JoinRule.Invite) {
joinRuleMicrocopy = <p>{_t("Only people invited will be able to find and join this space.")}</p>; joinRuleMicrocopy = <p>{_t("create_space|subspace_join_rule_invite_description")}</p>;
} }
return ( return (
<BaseDialog <BaseDialog
title={ title={
<SubspaceSelector <SubspaceSelector
title={_t("Create a space")} title={_t("create_space|subspace_dropdown_title")}
space={space} space={space}
value={parentSpace} value={parentSpace}
onChange={setParentSpace} onChange={setParentSpace}
@ -143,7 +143,7 @@ const CreateSubspaceDialog: React.FC<IProps> = ({ space, onAddExistingSpaceClick
<div className="mx_CreateSubspaceDialog_content"> <div className="mx_CreateSubspaceDialog_content">
<div className="mx_CreateSubspaceDialog_betaNotice"> <div className="mx_CreateSubspaceDialog_betaNotice">
<BetaPill /> <BetaPill />
{_t("Add a space to a space you manage.")} {_t("create_space|subspace_beta_notice")}
</div> </div>
<SpaceCreateForm <SpaceCreateForm
@ -161,8 +161,8 @@ const CreateSubspaceDialog: React.FC<IProps> = ({ space, onAddExistingSpaceClick
aliasFieldRef={spaceAliasField} aliasFieldRef={spaceAliasField}
> >
<JoinRuleDropdown <JoinRuleDropdown
label={_t("Space visibility")} label={_t("create_space|subspace_join_rule_label")}
labelInvite={_t("Private space (invite only)")} labelInvite={_t("create_space|subspace_join_rule_invite_only")}
labelPublic={_t("common|public_space")} labelPublic={_t("common|public_space")}
labelRestricted={_t("create_room|join_rule_restricted")} labelRestricted={_t("create_room|join_rule_restricted")}
width={478} width={478}
@ -175,7 +175,7 @@ const CreateSubspaceDialog: React.FC<IProps> = ({ space, onAddExistingSpaceClick
<div className="mx_CreateSubspaceDialog_footer"> <div className="mx_CreateSubspaceDialog_footer">
<div className="mx_CreateSubspaceDialog_footer_prompt"> <div className="mx_CreateSubspaceDialog_footer_prompt">
<div>{_t("Want to add an existing space instead?")}</div> <div>{_t("create_space|subspace_existing_space_prompt")}</div>
<AccessibleButton <AccessibleButton
kind="link" kind="link"
onClick={() => { onClick={() => {
@ -183,7 +183,7 @@ const CreateSubspaceDialog: React.FC<IProps> = ({ space, onAddExistingSpaceClick
onFinished(); onFinished();
}} }}
> >
{_t("Add existing space")} {_t("space|add_existing_subspace|space_dropdown_title")}
</AccessibleButton> </AccessibleButton>
</div> </div>
@ -191,7 +191,7 @@ const CreateSubspaceDialog: React.FC<IProps> = ({ space, onAddExistingSpaceClick
{_t("action|cancel")} {_t("action|cancel")}
</AccessibleButton> </AccessibleButton>
<AccessibleButton kind="primary" disabled={busy} onClick={onCreateSubspaceClick}> <AccessibleButton kind="primary" disabled={busy} onClick={onCreateSubspaceClick}>
{busy ? _t("Adding…") : _t("action|add")} {busy ? _t("create_space|subspace_adding") : _t("action|add")}
</AccessibleButton> </AccessibleButton>
</div> </div>
</MatrixClientContext.Provider> </MatrixClientContext.Provider>

View file

@ -35,10 +35,7 @@ const CryptoStoreTooNewDialog: React.FC<IProps> = (props: IProps) => {
const _onLogoutClicked = (): void => { const _onLogoutClicked = (): void => {
Modal.createDialog(QuestionDialog, { Modal.createDialog(QuestionDialog, {
title: _t("action|sign_out"), title: _t("action|sign_out"),
description: _t( description: _t("encryption|incompatible_database_sign_out_description", { brand }),
"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("action|sign_out"), button: _t("action|sign_out"),
focus: false, focus: false,
onFinished: (doLogout) => { onFinished: (doLogout) => {
@ -50,16 +47,13 @@ const CryptoStoreTooNewDialog: React.FC<IProps> = (props: IProps) => {
}); });
}; };
const description = _t( const description = _t("encryption|incompatible_database_description", { brand });
"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 },
);
return ( return (
<BaseDialog <BaseDialog
className="mx_CryptoStoreTooNewDialog" className="mx_CryptoStoreTooNewDialog"
contentId="mx_Dialog_content" contentId="mx_Dialog_content"
title={_t("Incompatible Database")} title={_t("encryption|incompatible_database_title")}
hasCancel={false} hasCancel={false}
onFinished={props.onFinished} onFinished={props.onFinished}
> >
@ -67,7 +61,7 @@ const CryptoStoreTooNewDialog: React.FC<IProps> = (props: IProps) => {
{description} {description}
</div> </div>
<DialogButtons <DialogButtons
primaryButton={_t("Continue With Encryption Disabled")} primaryButton={_t("encryption|incompatible_database_disable")}
hasCancel={false} hasCancel={false}
onPrimaryButtonClick={() => props.onFinished(false)} onPrimaryButtonClick={() => props.onFinished(false)}
> >

View file

@ -73,13 +73,13 @@ export default class DeactivateAccountDialog extends React.Component<IProps, ISt
private onStagePhaseChange = (stage: AuthType, phase: number): void => { private onStagePhaseChange = (stage: AuthType, phase: number): void => {
const dialogAesthetics = { const dialogAesthetics = {
[SSOAuthEntry.PHASE_PREAUTH]: { [SSOAuthEntry.PHASE_PREAUTH]: {
body: _t("Confirm your account deactivation by using Single Sign On to prove your identity."), body: _t("settings|general|deactivate_confirm_body_sso"),
continueText: _t("auth|sso"), continueText: _t("auth|sso"),
continueKind: "danger", continueKind: "danger",
}, },
[SSOAuthEntry.PHASE_POSTAUTH]: { [SSOAuthEntry.PHASE_POSTAUTH]: {
body: _t("Are you sure you want to deactivate your account? This is irreversible."), body: _t("settings|general|deactivate_confirm_body"),
continueText: _t("Confirm account deactivation"), continueText: _t("settings|general|deactivate_confirm_continue"),
continueKind: "danger", continueKind: "danger",
}, },
}; };
@ -90,7 +90,7 @@ export default class DeactivateAccountDialog extends React.Component<IProps, ISt
[SSOAuthEntry.UNSTABLE_LOGIN_TYPE]: dialogAesthetics, [SSOAuthEntry.UNSTABLE_LOGIN_TYPE]: dialogAesthetics,
[PasswordAuthEntry.LOGIN_TYPE]: { [PasswordAuthEntry.LOGIN_TYPE]: {
[DEFAULT_PHASE]: { [DEFAULT_PHASE]: {
body: _t("To continue, please enter your account password:"), body: _t("settings|general|deactivate_confirm_body_password"),
}, },
}, },
}; };
@ -122,7 +122,7 @@ export default class DeactivateAccountDialog extends React.Component<IProps, ISt
} }
logger.error("Error during UI Auth:", { result }); logger.error("Error during UI Auth:", { result });
this.setState({ errStr: _t("There was a problem communicating with the server. Please try again.") }); this.setState({ errStr: _t("settings|general|error_deactivate_communication") });
}; };
private onUIAuthComplete = (auth: IAuthData | null): void => { private onUIAuthComplete = (auth: IAuthData | null): void => {
@ -138,7 +138,7 @@ export default class DeactivateAccountDialog extends React.Component<IProps, ISt
}) })
.catch((e) => { .catch((e) => {
logger.error(e); logger.error(e);
this.setState({ errStr: _t("There was a problem communicating with the server. Please try again.") }); this.setState({ errStr: _t("settings|general|error_deactivate_communication") });
}); });
}; };
@ -170,14 +170,14 @@ export default class DeactivateAccountDialog extends React.Component<IProps, ISt
// We'll try to log something in an vain attempt to record what happened (storage // We'll try to log something in an vain attempt to record what happened (storage
// is also obliterated on logout). // is also obliterated on logout).
logger.warn("User's account got deactivated without confirmation: Server had no auth"); logger.warn("User's account got deactivated without confirmation: Server had no auth");
this.setState({ errStr: _t("Server did not require any authentication") }); this.setState({ errStr: _t("settings|general|error_deactivate_no_auth") });
}) })
.catch((e) => { .catch((e) => {
if (e && e.httpStatus === 401 && e.data) { if (e && e.httpStatus === 401 && e.data) {
// Valid UIA response // Valid UIA response
this.setState({ authData: e.data, authEnabled: true }); this.setState({ authData: e.data, authEnabled: true });
} else { } else {
this.setState({ errStr: _t("Server did not return valid authentication information.") }); this.setState({ errStr: _t("settings|general|error_deactivate_invalid_auth") });
} }
}); });
} }
@ -218,32 +218,20 @@ export default class DeactivateAccountDialog extends React.Component<IProps, ISt
screenName="DeactivateAccount" screenName="DeactivateAccount"
> >
<div className="mx_Dialog_content"> <div className="mx_Dialog_content">
<p>{_t("Confirm that you would like to deactivate your account. If you proceed:")}</p> <p>{_t("settings|general|deactivate_confirm_content")}</p>
<ul> <ul>
<li>{_t("You will not be able to reactivate your account")}</li> <li>{_t("settings|general|deactivate_confirm_content_1")}</li>
<li>{_t("You will no longer be able to log in")}</li> <li>{_t("settings|general|deactivate_confirm_content_2")}</li>
<li> <li>{_t("settings|general|deactivate_confirm_content_3")}</li>
{_t( <li>{_t("settings|general|deactivate_confirm_content_4")}</li>
"No one will be able to reuse your username (MXID), including you: this username will remain unavailable", <li>{_t("settings|general|deactivate_confirm_content_5")}</li>
)}
</li>
<li>{_t("You will leave all rooms and DMs that you are in")}</li>
<li>
{_t(
"You will be removed from the identity server: your friends will no longer be able to find you with your email or phone number",
)}
</li>
</ul> </ul>
<p> <p>{_t("settings|general|deactivate_confirm_content_6")}</p>
{_t(
"Your old messages will still be visible to people who received them, just like emails you sent in the past. Would you like to hide your sent messages from people who join rooms in the future?",
)}
</p>
<div className="mx_DeactivateAccountDialog_input_section"> <div className="mx_DeactivateAccountDialog_input_section">
<p> <p>
<StyledCheckbox checked={this.state.shouldErase} onChange={this.onEraseFieldChange}> <StyledCheckbox checked={this.state.shouldErase} onChange={this.onEraseFieldChange}>
{_t("Hide my messages from new joiners")} {_t("settings|general|deactivate_confirm_erase_label")}
</StyledCheckbox> </StyledCheckbox>
</p> </p>
{error} {error}

View file

@ -47,9 +47,7 @@ export default class EndPollDialog extends React.Component<IProps> {
const topAnswer = findTopAnswer(this.props.event, responses); const topAnswer = findTopAnswer(this.props.event, responses);
const message = const message =
topAnswer === "" topAnswer === "" ? _t("poll|end_message_no_votes") : _t("poll|end_message", { topAnswer });
? _t("The poll has ended. No votes were cast.")
: _t("The poll has ended. Top answer: %(topAnswer)s", { topAnswer });
const endEvent = PollEndEvent.from(this.props.event.getId()!, message).serialize(); const endEvent = PollEndEvent.from(this.props.event.getId()!, message).serialize();
@ -57,8 +55,8 @@ export default class EndPollDialog extends React.Component<IProps> {
} catch (e) { } catch (e) {
console.error("Failed to submit poll response event:", e); console.error("Failed to submit poll response event:", e);
Modal.createDialog(ErrorDialog, { Modal.createDialog(ErrorDialog, {
title: _t("Failed to end poll"), title: _t("poll|error_ending_title"),
description: _t("Sorry, the poll did not end. Please try again."), description: _t("poll|error_ending_description"),
}); });
} }
} }
@ -68,11 +66,9 @@ export default class EndPollDialog extends React.Component<IProps> {
public render(): React.ReactNode { public render(): React.ReactNode {
return ( return (
<QuestionDialog <QuestionDialog
title={_t("End Poll")} title={_t("poll|end_title")}
description={_t( description={_t("poll|end_description")}
"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("poll|end_title")}
)}
button={_t("End Poll")}
onFinished={(endPoll: boolean) => this.onFinished(endPoll)} onFinished={(endPoll: boolean) => this.onFinished(endPoll)}
/> />
); );

View file

@ -79,7 +79,7 @@ export default class ErrorDialog extends React.Component<IProps, IState> {
contentId="mx_Dialog_content" contentId="mx_Dialog_content"
> >
<div className="mx_Dialog_content" id="mx_Dialog_content"> <div className="mx_Dialog_content" id="mx_Dialog_content">
{this.props.description || _t("An error has occurred.")} {this.props.description || _t("error|dialog_description_default")}
</div> </div>
<div className="mx_Dialog_buttons"> <div className="mx_Dialog_buttons">
<button className="mx_Dialog_primary" onClick={this.onClick} autoFocus={this.props.focus}> <button className="mx_Dialog_primary" onClick={this.onClick} autoFocus={this.props.focus}>

View file

@ -278,7 +278,7 @@ const ExportDialog: React.FC<IProps> = ({ room, onFinished }) => {
); );
} }
const sizePostFix = <span>{_t("MB")}</span>; const sizePostFix = <span>{_t("export_chat|size_limit_postfix")}</span>;
if (exportCancelled) { if (exportCancelled) {
// Display successful cancellation message // Display successful cancellation message

View file

@ -57,7 +57,7 @@ const FeedbackDialog: React.FC<IProps> = (props: IProps) => {
Modal.createDialog(InfoDialog, { Modal.createDialog(InfoDialog, {
title: _t("feedback|sent"), title: _t("feedback|sent"),
description: _t("Thank you!"), description: _t("bug_reporting|thank_you"),
}); });
} }
props.onFinished(); props.onFinished();

View file

@ -115,17 +115,17 @@ const Entry: React.FC<IEntryProps> = ({ room, type, content, matrixClient: cli,
className = "mx_ForwardList_canSend"; className = "mx_ForwardList_canSend";
if (!room.maySendMessage()) { if (!room.maySendMessage()) {
disabled = true; disabled = true;
title = _t("You don't have permission to do this"); title = _t("forward|no_perms_title");
} }
} else if (sendState === SendState.Sending) { } else if (sendState === SendState.Sending) {
className = "mx_ForwardList_sending"; className = "mx_ForwardList_sending";
disabled = true; disabled = true;
title = _t("Sending"); title = _t("forward|sending");
icon = <div className="mx_ForwardList_sendIcon" aria-label={title} />; icon = <div className="mx_ForwardList_sendIcon" aria-label={title} />;
} else if (sendState === SendState.Sent) { } else if (sendState === SendState.Sent) {
className = "mx_ForwardList_sent"; className = "mx_ForwardList_sent";
disabled = true; disabled = true;
title = _t("Sent"); title = _t("forward|sent");
icon = <div className="mx_ForwardList_sendIcon" aria-label={title} />; icon = <div className="mx_ForwardList_sendIcon" aria-label={title} />;
} else { } else {
className = "mx_ForwardList_sendFailed"; className = "mx_ForwardList_sendFailed";
@ -139,7 +139,7 @@ const Entry: React.FC<IEntryProps> = ({ room, type, content, matrixClient: cli,
<AccessibleTooltipButton <AccessibleTooltipButton
className="mx_ForwardList_roomButton" className="mx_ForwardList_roomButton"
onClick={jumpToRoom} onClick={jumpToRoom}
title={_t("Open room")} title={_t("forward|open_room")}
alignment={Alignment.Top} alignment={Alignment.Top}
> >
<DecoratedRoomAvatar room={room} size="32px" /> <DecoratedRoomAvatar room={room} size="32px" />
@ -154,7 +154,7 @@ const Entry: React.FC<IEntryProps> = ({ room, type, content, matrixClient: cli,
title={title} title={title}
alignment={Alignment.Top} alignment={Alignment.Top}
> >
<div className="mx_ForwardList_sendLabel">{_t("Send")}</div> <div className="mx_ForwardList_sendLabel">{_t("forward|send_label")}</div>
{icon} {icon}
</AccessibleTooltipButton> </AccessibleTooltipButton>
</div> </div>
@ -256,7 +256,7 @@ const ForwardDialog: React.FC<IProps> = ({ matrixClient: cli, event, permalinkCr
const [truncateAt, setTruncateAt] = useState(20); const [truncateAt, setTruncateAt] = useState(20);
function overflowTile(overflowCount: number, totalCount: number): JSX.Element { function overflowTile(overflowCount: number, totalCount: number): JSX.Element {
const text = _t("and %(count)s others...", { count: overflowCount }); const text = _t("common|and_n_others", { count: overflowCount });
return ( return (
<EntityTile <EntityTile
className="mx_EntityTile_ellipsis" className="mx_EntityTile_ellipsis"
@ -279,7 +279,7 @@ const ForwardDialog: React.FC<IProps> = ({ matrixClient: cli, event, permalinkCr
onFinished={onFinished} onFinished={onFinished}
fixedWidth={false} fixedWidth={false}
> >
<h3>{_t("Message preview")}</h3> <h3>{_t("forward|message_preview_heading")}</h3>
<div <div
className={classnames("mx_ForwardDialog_preview", { className={classnames("mx_ForwardDialog_preview", {
mx_IRCLayout: previewLayout == Layout.IRC, mx_IRCLayout: previewLayout == Layout.IRC,
@ -297,7 +297,7 @@ const ForwardDialog: React.FC<IProps> = ({ matrixClient: cli, event, permalinkCr
<div className="mx_ForwardList" id="mx_ForwardList"> <div className="mx_ForwardList" id="mx_ForwardList">
<SearchBox <SearchBox
className="mx_textinput_icon mx_textinput_search" className="mx_textinput_icon mx_textinput_search"
placeholder={_t("Search for rooms or people")} placeholder={_t("forward|filter_placeholder")}
onSearch={setQuery} onSearch={setQuery}
autoFocus={true} autoFocus={true}
/> />

View file

@ -52,7 +52,7 @@ const GenericFeatureFeedbackDialog: React.FC<IProps> = ({
Modal.createDialog(InfoDialog, { Modal.createDialog(InfoDialog, {
title, title,
description: _t("Feedback sent! Thanks, we appreciate it!"), description: _t("feedback|sent"),
button: _t("action|close"), button: _t("action|close"),
hasCloseButton: false, hasCloseButton: false,
fixedWidth: false, fixedWidth: false,
@ -91,7 +91,7 @@ const GenericFeatureFeedbackDialog: React.FC<IProps> = ({
checked={canContact} checked={canContact}
onChange={(e) => setCanContact((e.target as HTMLInputElement).checked)} onChange={(e) => setCanContact((e.target as HTMLInputElement).checked)}
> >
{_t("You may contact me if you have any follow up questions")} {_t("feedback|can_contact_label")}
</StyledCheckbox> </StyledCheckbox>
</React.Fragment> </React.Fragment>
} }

View file

@ -176,31 +176,19 @@ export default class IncomingSasDialog extends React.Component<IProps, IState> {
} }
const userDetailText = [ const userDetailText = [
<p key="p1"> <p key="p1">{_t("encryption|verification|incoming_sas_user_dialog_text_1")}</p>,
{_t(
"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"> <p key="p2">
{_t( {_t(
// NB. Below wording adjusted to singular 'session' until we have // NB. Below wording adjusted to singular 'session' until we have
// cross-signing // cross-signing
"Verifying this user will mark their session as trusted, and also mark your session as trusted to them.", "encryption|verification|incoming_sas_user_dialog_text_2",
)} )}
</p>, </p>,
]; ];
const selfDetailText = [ const selfDetailText = [
<p key="p1"> <p key="p1">{_t("encryption|verification|incoming_sas_device_dialog_text_1")}</p>,
{_t( <p key="p2">{_t("encryption|verification|incoming_sas_device_dialog_text_2")}</p>,
"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.",
)}
</p>,
]; ];
return ( return (
@ -234,7 +222,7 @@ export default class IncomingSasDialog extends React.Component<IProps, IState> {
return ( return (
<div> <div>
<Spinner /> <Spinner />
<p>{_t("Waiting for partner to confirm…")}</p> <p>{_t("encryption|verification|incoming_sas_dialog_waiting")}</p>
</div> </div>
); );
} }
@ -268,7 +256,11 @@ export default class IncomingSasDialog extends React.Component<IProps, IState> {
} }
return ( return (
<BaseDialog title={_t("Incoming Verification Request")} onFinished={this.onFinished} fixedWidth={false}> <BaseDialog
title={_t("encryption|verification|incoming_sas_dialog_title")}
onFinished={this.onFinished}
fixedWidth={false}
>
{body} {body}
</BaseDialog> </BaseDialog>
); );

View file

@ -42,11 +42,11 @@ export default class IntegrationsDisabledDialog extends React.Component<IProps>
className="mx_IntegrationsDisabledDialog" className="mx_IntegrationsDisabledDialog"
hasCancel={true} hasCancel={true}
onFinished={this.props.onFinished} onFinished={this.props.onFinished}
title={_t("Integrations are disabled")} title={_t("integrations|disabled_dialog_title")}
> >
<div className="mx_IntegrationsDisabledDialog_content"> <div className="mx_IntegrationsDisabledDialog_content">
<p> <p>
{_t("Enable '%(manageIntegrations)s' in Settings to do this.", { {_t("integrations|disabled_dialog_description", {
manageIntegrations: _t("integration_manager|manage_title"), manageIntegrations: _t("integration_manager|manage_title"),
})} })}
</p> </p>

View file

@ -38,15 +38,10 @@ export default class IntegrationsImpossibleDialog extends React.Component<IProps
className="mx_IntegrationsImpossibleDialog" className="mx_IntegrationsImpossibleDialog"
hasCancel={false} hasCancel={false}
onFinished={this.props.onFinished} onFinished={this.props.onFinished}
title={_t("Integrations not allowed")} title={_t("integrations|impossible_dialog_title")}
> >
<div className="mx_IntegrationsImpossibleDialog_content"> <div className="mx_IntegrationsImpossibleDialog_content">
<p> <p>{_t("integrations|impossible_dialog_description", { brand })}</p>
{_t(
"Your %(brand)s doesn't allow you to use an integration manager to do this. Please contact an admin.",
{ brand },
)}
</p>
</div> </div>
<DialogButtons <DialogButtons
primaryButton={_t("action|ok")} primaryButton={_t("action|ok")}

View file

@ -98,13 +98,13 @@ export default class InteractiveAuthDialog<T> extends React.Component<Interactiv
const ssoAesthetics = { const ssoAesthetics = {
[SSOAuthEntry.PHASE_PREAUTH]: { [SSOAuthEntry.PHASE_PREAUTH]: {
title: _t("auth|uia|sso_title"), title: _t("auth|uia|sso_title"),
body: _t("To continue, use Single Sign On to prove your identity."), body: _t("auth|uia|sso_preauth_body"),
continueText: _t("auth|sso"), continueText: _t("auth|sso"),
continueKind: "primary", continueKind: "primary",
}, },
[SSOAuthEntry.PHASE_POSTAUTH]: { [SSOAuthEntry.PHASE_POSTAUTH]: {
title: _t("Confirm to continue"), title: _t("auth|uia|sso_postauth_title"),
body: _t("Click the button below to confirm your identity."), body: _t("auth|uia|sso_postauth_body"),
continueText: _t("action|confirm"), continueText: _t("action|confirm"),
continueKind: "primary", continueKind: "primary",
}, },

View file

@ -271,7 +271,7 @@ class DMRoomTile extends React.PureComponent<IDMRoomTileProps> {
}); });
const caption = (this.props.member as ThreepidMember).isEmail const caption = (this.props.member as ThreepidMember).isEmail
? _t("Invite by email") ? _t("invite|email_caption")
: this.highlightName(userIdentifier || this.props.member.userId); : this.highlightName(userIdentifier || this.props.member.userId);
return ( return (
@ -566,7 +566,7 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
logger.error(err); logger.error(err);
this.setState({ this.setState({
busy: false, busy: false,
errorText: _t("We couldn't create your DM."), errorText: _t("invite|error_dm"),
}); });
} }
}; };
@ -584,11 +584,9 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
onGiveUp: () => { onGiveUp: () => {
this.setBusy(false); this.setBusy(false);
}, },
description: _t( description: _t("invite|ask_anyway_description"),
"Unable to find profiles for the Matrix IDs listed below - would you like to start a DM anyway?", inviteNeverWarnLabel: _t("invite|ask_anyway_never_warn_label"),
), inviteLabel: _t("invite|ask_anyway_label"),
inviteNeverWarnLabel: _t("Start DM anyway and never warn me again"),
inviteLabel: _t("Start DM anyway"),
}); });
} }
@ -605,7 +603,7 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
logger.error("Failed to find the room to invite users to"); logger.error("Failed to find the room to invite users to");
this.setState({ this.setState({
busy: false, busy: false,
errorText: _t("Something went wrong trying to invite the users."), errorText: _t("invite|error_find_room"),
}); });
return; return;
} }
@ -620,9 +618,7 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
logger.error(err); logger.error(err);
this.setState({ this.setState({
busy: false, busy: false,
errorText: _t( errorText: _t("invite|error_invite"),
"We couldn't invite those users. Please check the users you want to invite and try again.",
),
}); });
} }
}; };
@ -635,7 +631,7 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
const targetIds = targets.map((t) => t.userId); const targetIds = targets.map((t) => t.userId);
if (targetIds.length > 1) { if (targetIds.length > 1) {
this.setState({ this.setState({
errorText: _t("A call can only be transferred to a single user."), errorText: _t("invite|error_transfer_multiple_target"),
}); });
return; return;
} }
@ -940,11 +936,8 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
if (failed.length > 0) { if (failed.length > 0) {
Modal.createDialog(QuestionDialog, { Modal.createDialog(QuestionDialog, {
title: _t("Failed to find the following users"), title: _t("invite|error_find_user_title"),
description: _t( description: _t("invite|error_find_user_description", { csvNames: failed.join(", ") }),
"The following users might not exist or are invalid, and cannot be invited: %(csvNames)s",
{ csvNames: failed.join(", ") },
),
button: _t("action|ok"), button: _t("action|ok"),
}); });
} }
@ -991,10 +984,10 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
let showNum = kind === "recents" ? this.state.numRecentsShown : this.state.numSuggestionsShown; let showNum = kind === "recents" ? this.state.numRecentsShown : this.state.numSuggestionsShown;
const showMoreFn = kind === "recents" ? this.showMoreRecents.bind(this) : this.showMoreSuggestions.bind(this); const showMoreFn = kind === "recents" ? this.showMoreRecents.bind(this) : this.showMoreSuggestions.bind(this);
const lastActive = (m: Result): number | undefined => (kind === "recents" ? m.lastActive : undefined); const lastActive = (m: Result): number | undefined => (kind === "recents" ? m.lastActive : undefined);
let sectionName = kind === "recents" ? _t("Recent Conversations") : _t("common|suggestions"); let sectionName = kind === "recents" ? _t("invite|recents_section") : _t("common|suggestions");
if (this.props.kind === InviteKind.Invite) { if (this.props.kind === InviteKind.Invite) {
sectionName = kind === "recents" ? _t("Recently Direct Messaged") : _t("common|suggestions"); sectionName = kind === "recents" ? _t("invite|suggestions_section") : _t("common|suggestions");
} }
// Mix in the server results if we have any, but only if we're searching. We track the additional // Mix in the server results if we have any, but only if we're searching. We track the additional
@ -1134,7 +1127,7 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
return ( return (
<div className="mx_InviteDialog_identityServer"> <div className="mx_InviteDialog_identityServer">
{_t( {_t(
"Use an identity server to invite by email. <default>Use the default (%(defaultIdentityServerName)s)</default> or manage in <settings>Settings</settings>.", "invite|email_use_default_is",
{ {
defaultIdentityServerName: abbreviateUrl(defaultIdentityServerUrl), defaultIdentityServerName: abbreviateUrl(defaultIdentityServerUrl),
}, },
@ -1157,7 +1150,7 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
return ( return (
<div className="mx_InviteDialog_identityServer"> <div className="mx_InviteDialog_identityServer">
{_t( {_t(
"Use an identity server to invite by email. Manage in <settings>Settings</settings>.", "invite|email_use_is",
{}, {},
{ {
settings: (sub) => ( settings: (sub) => (
@ -1281,11 +1274,11 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
const cli = MatrixClientPeg.safeGet(); const cli = MatrixClientPeg.safeGet();
const userId = cli.getUserId()!; const userId = cli.getUserId()!;
if (this.props.kind === InviteKind.Dm) { if (this.props.kind === InviteKind.Dm) {
title = _t("Direct Messages"); title = _t("space|add_existing_room_space|dm_heading");
if (identityServersEnabled) { if (identityServersEnabled) {
helpText = _t( helpText = _t(
"Start a conversation with someone using their name, email address or username (like <userId/>).", "invite|start_conversation_name_email_mxid_prompt",
{}, {},
{ {
userId: () => { userId: () => {
@ -1299,7 +1292,7 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
); );
} else { } else {
helpText = _t( helpText = _t(
"Start a conversation with someone using their name or username (like <userId/>).", "invite|start_conversation_name_mxid_prompt",
{}, {},
{ {
userId: () => { userId: () => {
@ -1317,14 +1310,14 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
goButtonFn = this.checkProfileAndStartDm; goButtonFn = this.checkProfileAndStartDm;
extraSection = ( extraSection = (
<div className="mx_InviteDialog_section_hidden_suggestions_disclaimer"> <div className="mx_InviteDialog_section_hidden_suggestions_disclaimer">
<span>{_t("Some suggestions may be hidden for privacy.")}</span> <span>{_t("invite|suggestions_disclaimer")}</span>
<p>{_t("If you can't see who you're looking for, send them your invite link below.")}</p> <p>{_t("invite|suggestions_disclaimer_prompt")}</p>
</div> </div>
); );
const link = makeUserPermalink(MatrixClientPeg.safeGet().getSafeUserId()); const link = makeUserPermalink(MatrixClientPeg.safeGet().getSafeUserId());
footer = ( footer = (
<div className="mx_InviteDialog_footer"> <div className="mx_InviteDialog_footer">
<h3>{_t("Or send invite link")}</h3> <h3>{_t("invite|send_link_prompt")}</h3>
<CopyableText getTextToCopy={() => makeUserPermalink(MatrixClientPeg.safeGet().getSafeUserId())}> <CopyableText getTextToCopy={() => makeUserPermalink(MatrixClientPeg.safeGet().getSafeUserId())}>
<a className="mx_InviteDialog_footer_link" href={link} onClick={this.onLinkClick}> <a className="mx_InviteDialog_footer_link" href={link} onClick={this.onLinkClick}>
{link} {link}
@ -1340,30 +1333,22 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
? _t("invite|to_space", { ? _t("invite|to_space", {
spaceName: room?.name || _t("common|unnamed_space"), spaceName: room?.name || _t("common|unnamed_space"),
}) })
: _t("Invite to %(roomName)s", { : _t("invite|to_room", {
roomName: room?.name || _t("common|unnamed_room"), roomName: room?.name || _t("common|unnamed_room"),
}); });
let helpTextUntranslated; let helpTextUntranslated;
if (isSpace) { if (isSpace) {
if (identityServersEnabled) { if (identityServersEnabled) {
helpTextUntranslated = _td( helpTextUntranslated = _td("invite|name_email_mxid_share_space");
"Invite someone using their name, email address, username (like <userId/>) or <a>share this space</a>.",
);
} else { } else {
helpTextUntranslated = _td( helpTextUntranslated = _td("invite|name_mxid_share_space");
"Invite someone using their name, username (like <userId/>) or <a>share this space</a>.",
);
} }
} else { } else {
if (identityServersEnabled) { if (identityServersEnabled) {
helpTextUntranslated = _td( helpTextUntranslated = _td("invite|name_email_mxid_share_room");
"Invite someone using their name, email address, username (like <userId/>) or <a>share this room</a>.",
);
} else { } else {
helpTextUntranslated = _td( helpTextUntranslated = _td("invite|name_mxid_share_room");
"Invite someone using their name, username (like <userId/>) or <a>share this room</a>.",
);
} }
} }
@ -1401,19 +1386,19 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
keySharingWarning = ( keySharingWarning = (
<p className="mx_InviteDialog_helpText"> <p className="mx_InviteDialog_helpText">
<InfoIcon height={14} width={14} /> <InfoIcon height={14} width={14} />
{" " + _t("Invited people will be able to read old messages.")} {" " + _t("invite|key_share_warning")}
</p> </p>
); );
} }
} }
} else if (this.props.kind === InviteKind.CallTransfer) { } else if (this.props.kind === InviteKind.CallTransfer) {
title = _t("Transfer"); title = _t("action|transfer");
consultConnectSection = ( consultConnectSection = (
<div className="mx_InviteDialog_transferConsultConnect"> <div className="mx_InviteDialog_transferConsultConnect">
<label> <label>
<input type="checkbox" checked={this.state.consultFirst} onChange={this.onConsultFirstChange} /> <input type="checkbox" checked={this.state.consultFirst} onChange={this.onConsultFirstChange} />
{_t("Consult first")} {_t("voip|transfer_consult_first_label")}
</label> </label>
<AccessibleButton <AccessibleButton
kind="secondary" kind="secondary"
@ -1427,7 +1412,7 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
onClick={this.transferCall} onClick={this.transferCall}
disabled={!hasSelection && this.state.dialPadValue === ""} disabled={!hasSelection && this.state.dialPadValue === ""}
> >
{_t("Transfer")} {_t("action|transfer")}
</AccessibleButton> </AccessibleButton>
</div> </div>
); );
@ -1450,11 +1435,7 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
if (!this.canInviteMore() || (this.hasFilterAtLeastOneEmail() && !this.canInviteThirdParty())) { if (!this.canInviteMore() || (this.hasFilterAtLeastOneEmail() && !this.canInviteThirdParty())) {
// We are in DM case here, because of the checks in canInviteMore() / canInviteThirdParty(). // We are in DM case here, because of the checks in canInviteMore() / canInviteThirdParty().
onlyOneThreepidNote = ( onlyOneThreepidNote = <div className="mx_InviteDialog_oneThreepid">{_t("invite|email_limit_one")}</div>;
<div className="mx_InviteDialog_oneThreepid">
{_t("Invites by email can only be sent one at a time")}
</div>
);
} else { } else {
results = ( results = (
<div className="mx_InviteDialog_userSections"> <div className="mx_InviteDialog_userSections">
@ -1487,7 +1468,12 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
let dialogContent; let dialogContent;
if (this.props.kind === InviteKind.CallTransfer) { if (this.props.kind === InviteKind.CallTransfer) {
const tabs: NonEmptyArray<Tab<TabId>> = [ const tabs: NonEmptyArray<Tab<TabId>> = [
new Tab(TabId.UserDirectory, _td("User Directory"), "mx_InviteDialog_userDirectoryIcon", usersSection), new Tab(
TabId.UserDirectory,
_td("invite|transfer_user_directory_tab"),
"mx_InviteDialog_userDirectoryIcon",
usersSection,
),
]; ];
const backspaceButton = <DialPadBackspaceButton onBackspacePress={this.onDeletePress} />; const backspaceButton = <DialPadBackspaceButton onBackspacePress={this.onDeletePress} />;
@ -1525,7 +1511,14 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
<Dialpad hasDial={false} onDigitPress={this.onDigitPress} onDeletePress={this.onDeletePress} /> <Dialpad hasDial={false} onDigitPress={this.onDigitPress} onDeletePress={this.onDeletePress} />
</div> </div>
); );
tabs.push(new Tab(TabId.DialPad, _td("Dial pad"), "mx_InviteDialog_dialPadIcon", dialPadSection)); tabs.push(
new Tab(
TabId.DialPad,
_td("invite|transfer_dial_pad_tab"),
"mx_InviteDialog_dialPadIcon",
dialPadSection,
),
);
dialogContent = ( dialogContent = (
<React.Fragment> <React.Fragment>
<TabbedView <TabbedView

View file

@ -47,11 +47,11 @@ const KeySignatureUploadFailedDialog: React.FC<IProps> = ({ failures, source, co
const onCancel = useRef(onFinished); const onCancel = useRef(onFinished);
const causes = new Map([ const causes = new Map([
["_afterCrossSigningLocalKeyChange", _t("a new master key signature")], ["_afterCrossSigningLocalKeyChange", _t("encryption|key_signature_upload_failed_master_key_signature")],
["checkOwnCrossSigningTrust", _t("a new cross-signing key signature")], ["checkOwnCrossSigningTrust", _t("encryption|key_signature_upload_failed_cross_signing_key_signature")],
["setDeviceVerification", _t("a device cross-signing signature")], ["setDeviceVerification", _t("encryption|key_signature_upload_failed_device_cross_signing_key_signature")],
]); ]);
const defaultCause = _t("a key signature"); const defaultCause = _t("encryption|key_signature_upload_failed_key_signature");
const onRetry = useCallback(async (): Promise<void> => { const onRetry = useCallback(async (): Promise<void> => {
try { try {
@ -78,7 +78,7 @@ const KeySignatureUploadFailedDialog: React.FC<IProps> = ({ failures, source, co
body = ( body = (
<div> <div>
<p>{_t("%(brand)s encountered an error during upload of:", { brand })}</p> <p>{_t("encryption|key_signature_upload_failed_body", { brand })}</p>
<p>{reason}</p> <p>{reason}</p>
{retrying && <Spinner />} {retrying && <Spinner />}
<pre>{JSON.stringify(failures, null, 2)}</pre> <pre>{JSON.stringify(failures, null, 2)}</pre>
@ -92,9 +92,11 @@ const KeySignatureUploadFailedDialog: React.FC<IProps> = ({ failures, source, co
</div> </div>
); );
} else { } else {
let text = _t("Upload completed"); let text = _t("encryption|key_signature_upload_completed");
if (!success) { if (!success) {
text = cancelled ? _t("Cancelled signature upload") : _t("Unable to upload"); text = cancelled
? _t("encryption|key_signature_upload_cancelled")
: _t("encryption|key_signature_upload_failed");
} }
body = ( body = (
@ -107,7 +109,11 @@ const KeySignatureUploadFailedDialog: React.FC<IProps> = ({ failures, source, co
return ( return (
<BaseDialog <BaseDialog
title={success ? _t("Signature upload success") : _t("Signature upload failed")} title={
success
? _t("encryption|key_signature_upload_success_title")
: _t("encryption|key_signature_upload_failed_title")
}
fixedWidth={false} fixedWidth={false}
onFinished={() => {}} onFinished={() => {}}
> >

View file

@ -28,31 +28,25 @@ interface IProps {
const LazyLoadingDisabledDialog: React.FC<IProps> = (props) => { const LazyLoadingDisabledDialog: React.FC<IProps> = (props) => {
const brand = SdkConfig.get().brand; const brand = SdkConfig.get().brand;
const description1 = _t( const description1 = _t("lazy_loading|disabled_description1", {
"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, brand,
host: props.host, host: props.host,
}, });
); const description2 = _t("lazy_loading|disabled_description2", {
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.",
{
brand, brand,
}, });
);
return ( return (
<QuestionDialog <QuestionDialog
hasCancelButton={false} hasCancelButton={false}
title={_t("Incompatible local cache")} title={_t("lazy_loading|disabled_title")}
description={ description={
<div> <div>
<p>{description1}</p> <p>{description1}</p>
<p>{description2}</p> <p>{description2}</p>
</div> </div>
} }
button={_t("Clear cache and resync")} button={_t("lazy_loading|disabled_action")}
onFinished={props.onFinished} onFinished={props.onFinished}
/> />
); );

View file

@ -27,15 +27,12 @@ interface IProps {
const LazyLoadingResyncDialog: React.FC<IProps> = (props) => { const LazyLoadingResyncDialog: React.FC<IProps> = (props) => {
const brand = SdkConfig.get().brand; const brand = SdkConfig.get().brand;
const description = _t( const description = _t("lazy_loading|resync_description", { brand });
"%(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 },
);
return ( return (
<QuestionDialog <QuestionDialog
hasCancelButton={false} hasCancelButton={false}
title={_t("Updating %(brand)s", { brand })} title={_t("lazy_loading|resync_title", { brand })}
description={<div>{description}</div>} description={<div>{description}</div>}
button={_t("action|ok")} button={_t("action|ok")}
onFinished={props.onFinished} onFinished={props.onFinished}

View file

@ -58,24 +58,22 @@ const LeaveSpaceDialog: React.FC<IProps> = ({ space, onFinished }) => {
let rejoinWarning; let rejoinWarning;
if (space.getJoinRule() !== JoinRule.Public) { if (space.getJoinRule() !== JoinRule.Public) {
rejoinWarning = _t("You won't be able to rejoin unless you are re-invited."); rejoinWarning = _t("space|leave_dialog_public_rejoin_warning");
} }
let onlyAdminWarning; let onlyAdminWarning;
if (isOnlyAdmin(space)) { 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("space|leave_dialog_only_admin_warning");
} else { } else {
const numChildrenOnlyAdminIn = roomsToLeave.filter(isOnlyAdmin).length; const numChildrenOnlyAdminIn = roomsToLeave.filter(isOnlyAdmin).length;
if (numChildrenOnlyAdminIn > 0) { if (numChildrenOnlyAdminIn > 0) {
onlyAdminWarning = _t( onlyAdminWarning = _t("space|leave_dialog_only_admin_room_warning");
"You're the only admin of some of the rooms or spaces you wish to leave. Leaving them will leave them without any admins.",
);
} }
} }
return ( return (
<BaseDialog <BaseDialog
title={_t("Leave %(spaceName)s", { spaceName: space.name })} title={_t("space|leave_dialog_title", { spaceName: space.name })}
className="mx_LeaveSpaceDialog" className="mx_LeaveSpaceDialog"
contentId="mx_LeaveSpaceDialog" contentId="mx_LeaveSpaceDialog"
onFinished={() => onFinished(false)} onFinished={() => onFinished(false)}
@ -84,7 +82,7 @@ const LeaveSpaceDialog: React.FC<IProps> = ({ space, onFinished }) => {
<div className="mx_Dialog_content" id="mx_LeaveSpaceDialog"> <div className="mx_Dialog_content" id="mx_LeaveSpaceDialog">
<p> <p>
{_t( {_t(
"You are about to leave <spaceName/>.", "space|leave_dialog_description",
{}, {},
{ {
spaceName: () => <b>{space.name}</b>, spaceName: () => <b>{space.name}</b>,
@ -93,7 +91,7 @@ const LeaveSpaceDialog: React.FC<IProps> = ({ space, onFinished }) => {
&nbsp; &nbsp;
{rejoinWarning} {rejoinWarning}
{rejoinWarning && <>&nbsp;</>} {rejoinWarning && <>&nbsp;</>}
{spaceChildren.length > 0 && _t("Would you like to leave the rooms in this space?")} {spaceChildren.length > 0 && _t("space|leave_dialog_option_intro")}
</p> </p>
{spaceChildren.length > 0 && ( {spaceChildren.length > 0 && (
@ -102,16 +100,16 @@ const LeaveSpaceDialog: React.FC<IProps> = ({ space, onFinished }) => {
spaceChildren={spaceChildren} spaceChildren={spaceChildren}
selected={selectedRooms} selected={selectedRooms}
onChange={setRoomsToLeave} onChange={setRoomsToLeave}
noneLabel={_t("Don't leave any rooms")} noneLabel={_t("space|leave_dialog_option_none")}
allLabel={_t("Leave all rooms")} allLabel={_t("space|leave_dialog_option_all")}
specificLabel={_t("Leave some rooms")} specificLabel={_t("space|leave_dialog_option_specific")}
/> />
)} )}
{onlyAdminWarning && <div className="mx_LeaveSpaceDialog_section_warning">{onlyAdminWarning}</div>} {onlyAdminWarning && <div className="mx_LeaveSpaceDialog_section_warning">{onlyAdminWarning}</div>}
</div> </div>
<DialogButtons <DialogButtons
primaryButton={_t("Leave space")} primaryButton={_t("space|leave_dialog_action")}
primaryButtonClass="danger" primaryButtonClass="danger"
onPrimaryButtonClick={() => onFinished(true, roomsToLeave)} onPrimaryButtonClick={() => onFinished(true, roomsToLeave)}
hasCancel={true} hasCancel={true}

View file

@ -166,16 +166,8 @@ export default class LogoutDialog extends React.Component<IProps, IState> {
private renderSetupBackupDialog(): React.ReactNode { private renderSetupBackupDialog(): React.ReactNode {
const description = ( const description = (
<div> <div>
<p> <p>{_t("auth|logout_dialog|setup_secure_backup_description_1")}</p>
{_t( <p>{_t("auth|logout_dialog|setup_secure_backup_description_2")}</p>
"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.",
)}
</p>
<p>{_t("encryption|setup_secure_backup|explainer")}</p> <p>{_t("encryption|setup_secure_backup|explainer")}</p>
</div> </div>
); );
@ -186,7 +178,7 @@ export default class LogoutDialog extends React.Component<IProps, IState> {
} else { } else {
// if there's an error fetching the backup info, we'll just assume there's // if there's an error fetching the backup info, we'll just assume there's
// no backup for the purpose of the button caption // no backup for the purpose of the button caption
setupButtonCaption = _t("Start using Key Backup"); setupButtonCaption = _t("auth|logout_dialog|use_key_backup");
} }
const dialogContent = ( const dialogContent = (
@ -200,12 +192,12 @@ export default class LogoutDialog extends React.Component<IProps, IState> {
onPrimaryButtonClick={this.onSetRecoveryMethodClick} onPrimaryButtonClick={this.onSetRecoveryMethodClick}
focus={true} focus={true}
> >
<button onClick={this.onLogoutConfirm}>{_t("I don't want my encrypted messages")}</button> <button onClick={this.onLogoutConfirm}>{_t("auth|logout_dialog|skip_key_backup")}</button>
</DialogButtons> </DialogButtons>
<details> <details>
<summary>{_t("common|advanced")}</summary> <summary>{_t("common|advanced")}</summary>
<p> <p>
<button onClick={this.onExportE2eKeysClicked}>{_t("Manually export keys")}</button> <button onClick={this.onExportE2eKeysClicked}>{_t("auth|logout_dialog|megolm_export")}</button>
</p> </p>
</details> </details>
</div> </div>
@ -215,7 +207,7 @@ export default class LogoutDialog extends React.Component<IProps, IState> {
// confirms the action. // confirms the action.
return ( return (
<BaseDialog <BaseDialog
title={_t("You'll lose access to your encrypted messages")} title={_t("auth|logout_dialog|setup_key_backup_title")}
contentId="mx_Dialog_content" contentId="mx_Dialog_content"
hasCancel={true} hasCancel={true}
onFinished={this.onFinished} onFinished={this.onFinished}
@ -246,7 +238,7 @@ export default class LogoutDialog extends React.Component<IProps, IState> {
<QuestionDialog <QuestionDialog
hasCancelButton={true} hasCancelButton={true}
title={_t("action|sign_out")} title={_t("action|sign_out")}
description={_t("Are you sure you want to sign out?")} description={_t("auth|logout_dialog|description")}
button={_t("action|sign_out")} button={_t("action|sign_out")}
onFinished={this.onFinished} onFinished={this.onFinished}
/> />

View file

@ -43,10 +43,10 @@ const Entry: React.FC<{
let description; let description;
if (localRoom) { if (localRoom) {
description = _t("%(count)s members", { count: room.getJoinedMemberCount() }); description = _t("common|n_members", { count: room.getJoinedMemberCount() });
const numChildRooms = SpaceStore.instance.getChildRooms(room.roomId).length; const numChildRooms = SpaceStore.instance.getChildRooms(room.roomId).length;
if (numChildRooms > 0) { if (numChildRooms > 0) {
description += " · " + _t("%(count)s rooms", { count: numChildRooms }); description += " · " + _t("common|n_rooms", { count: numChildRooms });
} }
} }
@ -132,7 +132,7 @@ const ManageRestrictedJoinRuleDialog: React.FC<IProps> = ({ room, selected = [],
if (newSelected.size < 1) { if (newSelected.size < 1) {
inviteOnlyWarning = ( inviteOnlyWarning = (
<div className="mx_ManageRestrictedJoinRuleDialog_section_info"> <div className="mx_ManageRestrictedJoinRuleDialog_section_info">
{_t("You're removing all spaces. Access will default to invite only")} {_t("room_settings|security|join_rule_restricted_dialog_empty_warning")}
</div> </div>
); );
} }
@ -141,14 +141,14 @@ const ManageRestrictedJoinRuleDialog: React.FC<IProps> = ({ room, selected = [],
filteredSpacesContainingRoom.length + filteredOtherJoinedSpaces.length + filteredOtherEntries.length; filteredSpacesContainingRoom.length + filteredOtherJoinedSpaces.length + filteredOtherEntries.length;
return ( return (
<BaseDialog <BaseDialog
title={_t("Select spaces")} title={_t("room_settings|security|join_rule_restricted_dialog_title")}
className="mx_ManageRestrictedJoinRuleDialog" className="mx_ManageRestrictedJoinRuleDialog"
onFinished={onFinished} onFinished={onFinished}
fixedWidth={false} fixedWidth={false}
> >
<p> <p>
{_t( {_t(
"Decide which spaces can access this room. If a space is selected, its members can find and join <RoomName/>.", "room_settings|security|join_rule_restricted_dialog_description",
{}, {},
{ {
RoomName: () => <b>{room.name}</b>, RoomName: () => <b>{room.name}</b>,
@ -158,7 +158,7 @@ const ManageRestrictedJoinRuleDialog: React.FC<IProps> = ({ room, selected = [],
<MatrixClientContext.Provider value={cli}> <MatrixClientContext.Provider value={cli}>
<SearchBox <SearchBox
className="mx_textinput_icon mx_textinput_search" className="mx_textinput_icon mx_textinput_search"
placeholder={_t("Search spaces")} placeholder={_t("room_settings|security|join_rule_restricted_dialog_filter_placeholder")}
onSearch={setQuery} onSearch={setQuery}
autoFocus={true} autoFocus={true}
/> />
@ -167,8 +167,8 @@ const ManageRestrictedJoinRuleDialog: React.FC<IProps> = ({ room, selected = [],
<div className="mx_ManageRestrictedJoinRuleDialog_section"> <div className="mx_ManageRestrictedJoinRuleDialog_section">
<h3> <h3>
{room.isSpaceRoom() {room.isSpaceRoom()
? _t("Spaces you know that contain this space") ? _t("room_settings|security|join_rule_restricted_dialog_heading_space")
: _t("Spaces you know that contain this room")} : _t("room_settings|security|join_rule_restricted_dialog_heading_room")}
</h3> </h3>
{filteredSpacesContainingRoom.map((space) => { {filteredSpacesContainingRoom.map((space) => {
return ( return (
@ -187,9 +187,9 @@ const ManageRestrictedJoinRuleDialog: React.FC<IProps> = ({ room, selected = [],
{filteredOtherEntries.length > 0 ? ( {filteredOtherEntries.length > 0 ? (
<div className="mx_ManageRestrictedJoinRuleDialog_section"> <div className="mx_ManageRestrictedJoinRuleDialog_section">
<h3>{_t("Other spaces or rooms you might not know")}</h3> <h3>{_t("room_settings|security|join_rule_restricted_dialog_heading_other")}</h3>
<div className="mx_ManageRestrictedJoinRuleDialog_section_info"> <div className="mx_ManageRestrictedJoinRuleDialog_section_info">
<div>{_t("These are likely ones other room admins are a part of.")}</div> <div>{_t("room_settings|security|join_rule_restricted_dialog_heading_unknown")}</div>
</div> </div>
{filteredOtherEntries.map((space) => { {filteredOtherEntries.map((space) => {
return ( return (
@ -208,7 +208,7 @@ const ManageRestrictedJoinRuleDialog: React.FC<IProps> = ({ room, selected = [],
{filteredOtherJoinedSpaces.length > 0 ? ( {filteredOtherJoinedSpaces.length > 0 ? (
<div className="mx_ManageRestrictedJoinRuleDialog_section"> <div className="mx_ManageRestrictedJoinRuleDialog_section">
<h3>{_t("Other spaces you know")}</h3> <h3>{_t("room_settings|security|join_rule_restricted_dialog_heading_known")}</h3>
{filteredOtherJoinedSpaces.map((space) => { {filteredOtherJoinedSpaces.map((space) => {
return ( return (
<Entry <Entry

View file

@ -51,9 +51,9 @@ export function ManualDeviceKeyVerificationDialog({
let text; let text;
if (mxClient?.getUserId() === userId) { if (mxClient?.getUserId() === userId) {
text = _t("Confirm by comparing the following with the User Settings in your other session:"); text = _t("encryption|verification|manual_device_verification_self_text");
} else { } else {
text = _t("Confirm this user's session by comparing the following with their User Settings:"); text = _t("encryption|verification|manual_device_verification_user_text");
} }
const fingerprint = device.getFingerprint(); const fingerprint = device.getFingerprint();
@ -64,16 +64,17 @@ export function ManualDeviceKeyVerificationDialog({
<div className="mx_DeviceVerifyDialog_cryptoSection"> <div className="mx_DeviceVerifyDialog_cryptoSection">
<ul> <ul>
<li> <li>
<label>{_t("Session name")}:</label> <span>{device.displayName}</span> <label>{_t("encryption|verification|manual_device_verification_device_name_label")}:</label>{" "}
<span>{device.displayName}</span>
</li> </li>
<li> <li>
<label>{_t("Session ID")}:</label>{" "} <label>{_t("encryption|verification|manual_device_verification_device_id_label")}:</label>{" "}
<span> <span>
<code>{device.deviceId}</code> <code>{device.deviceId}</code>
</span> </span>
</li> </li>
<li> <li>
<label>{_t("Session key")}:</label>{" "} <label>{_t("encryption|verification|manual_device_verification_device_key_label")}:</label>{" "}
<span> <span>
<code> <code>
<b>{key}</b> <b>{key}</b>
@ -82,7 +83,7 @@ export function ManualDeviceKeyVerificationDialog({
</li> </li>
</ul> </ul>
</div> </div>
<p>{_t("If they don't match, the security of your communication may be compromised.")}</p> <p>{_t("encryption|verification|manual_device_verification_footer")}</p>
</div> </div>
); );

View file

@ -154,11 +154,7 @@ export default class MessageEditHistoryDialog extends React.PureComponent<IProps
if (this.state.error) { if (this.state.error) {
const { error } = this.state; const { error } = this.state;
if (error.errcode === "M_UNRECOGNIZED") { if (error.errcode === "M_UNRECOGNIZED") {
content = ( content = <p className="mx_MessageEditHistoryDialog_error">{_t("error|edit_history_unsupported")}</p>;
<p className="mx_MessageEditHistoryDialog_error">
{_t("Your homeserver doesn't seem to support this feature.")}
</p>
);
} else if (error.errcode) { } else if (error.errcode) {
// some kind of error from the homeserver // some kind of error from the homeserver
content = <p className="mx_MessageEditHistoryDialog_error">{_t("error|something_went_wrong")}</p>; content = <p className="mx_MessageEditHistoryDialog_error">{_t("error|something_went_wrong")}</p>;
@ -190,7 +186,7 @@ export default class MessageEditHistoryDialog extends React.PureComponent<IProps
className="mx_MessageEditHistoryDialog" className="mx_MessageEditHistoryDialog"
hasCancel={true} hasCancel={true}
onFinished={this.props.onFinished} onFinished={this.props.onFinished}
title={_t("Message edits")} title={_t("message_edit_dialog_title")}
> >
{content} {content}
</BaseDialog> </BaseDialog>

View file

@ -187,7 +187,7 @@ export default class ModalWidgetDialog extends React.PureComponent<IProps, IStat
return ( return (
<BaseDialog <BaseDialog
title={this.props.widgetDefinition.name || _t("Modal Widget")} title={this.props.widgetDefinition.name || _t("widget|modal_title_default")}
className="mx_ModalWidgetDialog" className="mx_ModalWidgetDialog"
contentId="mx_Dialog_content" contentId="mx_Dialog_content"
onFinished={this.props.onFinished} onFinished={this.props.onFinished}
@ -199,7 +199,7 @@ export default class ModalWidgetDialog extends React.PureComponent<IProps, IStat
width="16" width="16"
alt="" alt=""
/> />
{_t("Data on this screen is shared with %(widgetDomain)s", { {_t("widget|modal_data_warning", {
widgetDomain: parsed.hostname, widgetDomain: parsed.hostname,
})} })}
</div> </div>

View file

@ -50,7 +50,7 @@ const RegistrationEmailPromptDialog: React.FC<IProps> = ({ onFinished }) => {
return ( return (
<BaseDialog <BaseDialog
title={_t("Continuing without email")} title={_t("auth|registration|continue_without_email_title")}
className="mx_RegistrationEmailPromptDialog" className="mx_RegistrationEmailPromptDialog"
contentId="mx_RegistrationEmailPromptDialog" contentId="mx_RegistrationEmailPromptDialog"
onFinished={() => onFinished(false)} onFinished={() => onFinished(false)}
@ -59,7 +59,7 @@ const RegistrationEmailPromptDialog: React.FC<IProps> = ({ onFinished }) => {
<div className="mx_Dialog_content" id="mx_RegistrationEmailPromptDialog"> <div className="mx_Dialog_content" id="mx_RegistrationEmailPromptDialog">
<p> <p>
{_t( {_t(
"Just a heads up, if you don't add an email and forget your password, you could <b>permanently lose access to your account</b>.", "auth|registration|continue_without_email_description",
{}, {},
{ {
b: (sub) => <b>{sub}</b>, b: (sub) => <b>{sub}</b>,
@ -70,7 +70,7 @@ const RegistrationEmailPromptDialog: React.FC<IProps> = ({ onFinished }) => {
<EmailField <EmailField
fieldRef={fieldRef} fieldRef={fieldRef}
autoFocus={true} autoFocus={true}
label={_td("Email (optional)")} label={_td("auth|registration|continue_without_email_field_label")}
value={email} value={email}
onChange={(ev) => { onChange={(ev) => {
const target = ev.target as HTMLInputElement; const target = ev.target as HTMLInputElement;

View file

@ -246,7 +246,7 @@ export default class ReportEventDialog extends React.Component<IProps, IState> {
// as configured in the room's state events. // as configured in the room's state events.
const dmRoomId = await ensureDMExists(client, this.moderation.moderationBotUserId); const dmRoomId = await ensureDMExists(client, this.moderation.moderationBotUserId);
if (!dmRoomId) { if (!dmRoomId) {
throw new UserFriendlyError("Unable to create room with moderation bot"); throw new UserFriendlyError("report_content|error_create_room_moderation_bot");
} }
await client.sendEvent(dmRoomId, ABUSE_EVENT_TYPE, { await client.sendEvent(dmRoomId, ABUSE_EVENT_TYPE, {
@ -320,37 +320,25 @@ export default class ReportEventDialog extends React.Component<IProps, IState> {
subtitle = _t("report_content|nature_disagreement"); subtitle = _t("report_content|nature_disagreement");
break; break;
case Nature.Toxic: case Nature.Toxic:
subtitle = _t( subtitle = _t("report_content|nature_toxic");
"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; break;
case Nature.Illegal: case Nature.Illegal:
subtitle = _t( subtitle = _t("report_content|nature_illegal");
"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; break;
case Nature.Spam: case Nature.Spam:
subtitle = _t( subtitle = _t("report_content|nature_spam");
"This user is spamming the room with ads, links to ads or to propaganda.\nThis will be reported to the room moderators.",
);
break; break;
case NonStandardValue.Admin: case NonStandardValue.Admin:
if (client.isRoomEncrypted(this.props.mxEvent.getRoomId()!)) { if (client.isRoomEncrypted(this.props.mxEvent.getRoomId()!)) {
subtitle = _t( subtitle = _t("report_content|nature_nonstandard_admin_encrypted", {
"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,
{ homeserver: homeServerName }, });
);
} else { } else {
subtitle = _t( subtitle = _t("report_content|nature_nonstandard_admin", { homeserver: homeServerName });
"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; break;
case Nature.Other: case Nature.Other:
subtitle = _t( subtitle = _t("report_content|nature_other");
"Any other reason. Please describe the problem.\nThis will be reported to the room moderators.",
);
break; break;
default: default:
subtitle = _t("report_content|nature"); subtitle = _t("report_content|nature");
@ -411,7 +399,7 @@ export default class ReportEventDialog extends React.Component<IProps, IState> {
checked={this.state.nature == Nature.Other} checked={this.state.nature == Nature.Other}
onChange={this.onNatureChosen} onChange={this.onNatureChosen}
> >
{_t("Other")} {_t("report_content|other_label")}
</StyledRadioButton> </StyledRadioButton>
<p>{subtitle}</p> <p>{subtitle}</p>
<Field <Field
@ -447,11 +435,7 @@ export default class ReportEventDialog extends React.Component<IProps, IState> {
contentId="mx_ReportEventDialog" contentId="mx_ReportEventDialog"
> >
<div className="mx_ReportEventDialog" id="mx_ReportEventDialog"> <div className="mx_ReportEventDialog" id="mx_ReportEventDialog">
<p> <p>{_t("report_content|description")}</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.",
)}
</p>
{adminMessage} {adminMessage}
<Field <Field
className="mx_ReportEventDialog_reason" className="mx_ReportEventDialog_reason"

View file

@ -241,7 +241,7 @@ class RoomSettingsDialog extends React.Component<IProps, IState> {
className="mx_RoomSettingsDialog" className="mx_RoomSettingsDialog"
hasCancel={true} hasCancel={true}
onFinished={this.props.onFinished} onFinished={this.props.onFinished}
title={_t("Room Settings - %(roomName)s", { roomName })} title={_t("room_settings|title", { roomName })}
> >
<div className="mx_SettingsDialog_content"> <div className="mx_SettingsDialog_content">
<TabbedView <TabbedView

View file

@ -59,8 +59,9 @@ export default class RoomUpgradeDialog extends React.Component<IProps, IState> {
}) })
.catch((err) => { .catch((err) => {
Modal.createDialog(ErrorDialog, { Modal.createDialog(ErrorDialog, {
title: _t("Failed to upgrade room"), title: _t("room_settings|advanced|error_upgrade_title"),
description: err && err.message ? err.message : _t("The room upgrade could not be completed"), description:
err && err.message ? err.message : _t("room_settings|advanced|error_upgrade_description"),
}); });
}) })
.finally(() => { .finally(() => {
@ -75,7 +76,7 @@ export default class RoomUpgradeDialog extends React.Component<IProps, IState> {
} else { } else {
buttons = ( buttons = (
<DialogButtons <DialogButtons
primaryButton={_t("Upgrade this room to version %(version)s", { version: this.targetVersion })} primaryButton={_t("room_settings|advanced|upgrade_button", { version: this.targetVersion })}
primaryButtonClass="danger" primaryButtonClass="danger"
hasCancel={true} hasCancel={true}
onPrimaryButtonClick={this.onUpgradeClick} onPrimaryButtonClick={this.onUpgradeClick}
@ -88,28 +89,16 @@ export default class RoomUpgradeDialog extends React.Component<IProps, IState> {
<BaseDialog <BaseDialog
className="mx_RoomUpgradeDialog" className="mx_RoomUpgradeDialog"
onFinished={this.props.onFinished} onFinished={this.props.onFinished}
title={_t("Upgrade Room Version")} title={_t("room_settings|advanced|upgrade_dialog_title")}
contentId="mx_Dialog_content" contentId="mx_Dialog_content"
hasCancel={true} hasCancel={true}
> >
<p> <p>{_t("room_settings|advanced|upgrade_dialog_description")}</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:",
)}
</p>
<ol> <ol>
<li>{_t("Create a new room with the same name, description and avatar")}</li> <li>{_t("room_settings|advanced|upgrade_dialog_description_1")}</li>
<li>{_t("Update any local room aliases to point to the new room")}</li> <li>{_t("room_settings|advanced|upgrade_dialog_description_2")}</li>
<li> <li>{_t("room_settings|advanced|upgrade_dialog_description_3")}</li>
{_t( <li>{_t("room_settings|advanced|upgrade_dialog_description_4")}</li>
"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",
)}
</li>
</ol> </ol>
{buttons} {buttons}
</BaseDialog> </BaseDialog>

View file

@ -115,7 +115,7 @@ export default class RoomUpgradeWarningDialog extends React.Component<IProps, IS
<LabelledToggleSwitch <LabelledToggleSwitch
value={this.state.inviteUsersToNewRoom} value={this.state.inviteUsersToNewRoom}
onChange={this.onInviteUsersToggle} onChange={this.onInviteUsersToggle}
label={_t("Automatically invite members from this room to the new one")} label={_t("room_settings|advanced|upgrade_warning_dialog_invite_label")}
/> />
); );
} }
@ -123,28 +123,21 @@ export default class RoomUpgradeWarningDialog extends React.Component<IProps, IS
let title: string; let title: string;
switch (this.joinRule) { switch (this.joinRule) {
case JoinRule.Invite: case JoinRule.Invite:
title = _t("Upgrade private room"); title = _t("room_settings|advanced|upgrade_warning_dialog_title_private");
break; break;
case JoinRule.Public: case JoinRule.Public:
title = _t("Upgrade public room"); title = _t("room_settings|advanced|upgrade_dwarning_ialog_title_public");
break; break;
default: default:
title = _t("Upgrade room"); title = _t("room_settings|advanced|upgrade_warning_dialog_title");
} }
let bugReports = ( let bugReports = <p>{_t("room_settings|advanced|upgrade_warning_dialog_report_bug_prompt", { brand })}</p>;
<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.",
{ brand },
)}
</p>
);
if (SdkConfig.get().bug_report_endpoint_url) { if (SdkConfig.get().bug_report_endpoint_url) {
bugReports = ( bugReports = (
<p> <p>
{_t( {_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>.", "room_settings|advanced|upgrade_warning_dialog_report_bug_prompt_link",
{ {
brand, brand,
}, },
@ -190,15 +183,10 @@ export default class RoomUpgradeWarningDialog extends React.Component<IProps, IS
title={title} title={title}
> >
<div> <div>
<p> <p>{this.props.description || _t("room_settings|advanced|upgrade_warning_dialog_description")}</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.",
)}
</p>
<p> <p>
{_t( {_t(
"<b>Please note upgrading will make a new version of the room</b>. All current messages will stay in this archived room.", "room_settings|advanced|upgrade_warning_dialog_explainer",
{}, {},
{ {
b: (sub) => <b>{sub}</b>, b: (sub) => <b>{sub}</b>,
@ -208,7 +196,7 @@ export default class RoomUpgradeWarningDialog extends React.Component<IProps, IS
{bugReports} {bugReports}
<p> <p>
{_t( {_t(
"You'll upgrade this room from <oldVersion /> to <newVersion />.", "room_settings|advanced|upgrade_warning_dialog_footer",
{}, {},
{ {
oldVersion: () => <code>{this.currentVersion}</code>, oldVersion: () => <code>{this.currentVersion}</code>,

View file

@ -97,7 +97,7 @@ export default abstract class ScrollableBaseModal<
<AccessibleButton <AccessibleButton
onClick={this.onCancel} onClick={this.onCancel}
className="mx_CompoundDialog_cancelButton" className="mx_CompoundDialog_cancelButton"
aria-label={_t("Close dialog")} aria-label={_t("dialog_close_label")}
/> />
</div> </div>
<form onSubmit={this.onSubmit} className="mx_CompoundDialog_form"> <form onSubmit={this.onSubmit} className="mx_CompoundDialog_form">

View file

@ -92,36 +92,32 @@ export default class ServerOfflineDialog extends React.PureComponent<IProps> {
public render(): React.ReactNode { public render(): React.ReactNode {
let timeline = this.renderTimeline().filter((c) => !!c); // remove nulls for next check let timeline = this.renderTimeline().filter((c) => !!c); // remove nulls for next check
if (timeline.length === 0) { if (timeline.length === 0) {
timeline = [<div key={1}>{_t("You're all caught up.")}</div>]; timeline = [<div key={1}>{_t("server_offline|empty_timeline")}</div>];
} }
const serverName = MatrixClientPeg.getHomeserverName(); const serverName = MatrixClientPeg.getHomeserverName();
return ( return (
<BaseDialog <BaseDialog
title={_t("Server isn't responding")} title={_t("server_offline|title")}
className="mx_ServerOfflineDialog" className="mx_ServerOfflineDialog"
contentId="mx_Dialog_content" contentId="mx_Dialog_content"
onFinished={this.props.onFinished} onFinished={this.props.onFinished}
hasCancel={true} hasCancel={true}
> >
<div className="mx_ServerOfflineDialog_content"> <div className="mx_ServerOfflineDialog_content">
<p> <p>{_t("server_offline|description")}</p>
{_t(
"Your server isn't responding to some of your requests. Below are some of the most likely reasons.",
)}
</p>
<ul> <ul>
<li>{_t("The server (%(serverName)s) took too long to respond.", { serverName })}</li> <li>{_t("server_offline|description_1", { serverName })}</li>
<li>{_t("Your firewall or anti-virus is blocking the request.")}</li> <li>{_t("server_offline|description_2")}</li>
<li>{_t("A browser extension is preventing the request.")}</li> <li>{_t("server_offline|description_3")}</li>
<li>{_t("The server is offline.")}</li> <li>{_t("server_offline|description_4")}</li>
<li>{_t("The server has denied your request.")}</li> <li>{_t("server_offline|description_5")}</li>
<li>{_t("Your area is experiencing difficulties connecting to the internet.")}</li> <li>{_t("server_offline|description_6")}</li>
<li>{_t("A connection error occurred while trying to contact the server.")}</li> <li>{_t("server_offline|description_7")}</li>
<li>{_t("The server is not configured to indicate what the problem is (CORS).")}</li> <li>{_t("server_offline|description_8")}</li>
</ul> </ul>
<hr /> <hr />
<h2>{_t("Recent changes that have not yet been received")}</h2> <h2>{_t("server_offline|recent_changes_heading")}</h2>
{timeline} {timeline}
</div> </div>
</BaseDialog> </BaseDialog>

View file

@ -30,19 +30,17 @@ export default class SeshatResetDialog extends React.PureComponent<Props> {
<BaseDialog <BaseDialog
hasCancel={true} hasCancel={true}
onFinished={this.props.onFinished.bind(null, false)} onFinished={this.props.onFinished.bind(null, false)}
title={_t("Reset event store?")} title={_t("seshat|reset_title")}
> >
<div> <div>
<p> <p>
{_t("You most likely do not want to reset your event index store")} {_t("seshat|reset_description")}
<br /> <br />
{_t( {_t("seshat|reset_explainer")}
"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> </p>
</div> </div>
<DialogButtons <DialogButtons
primaryButton={_t("Reset event store")} primaryButton={_t("seshat|reset_button")}
onPrimaryButtonClick={this.props.onFinished.bind(null, true)} onPrimaryButtonClick={this.props.onFinished.bind(null, true)}
primaryButtonClass="danger" primaryButtonClass="danger"
cancelButton={_t("action|cancel")} cancelButton={_t("action|cancel")}

View file

@ -41,7 +41,7 @@ export default class SessionRestoreErrorDialog extends React.Component<IProps> {
private onClearStorageClick = (): void => { private onClearStorageClick = (): void => {
Modal.createDialog(QuestionDialog, { Modal.createDialog(QuestionDialog, {
title: _t("action|sign_out"), title: _t("action|sign_out"),
description: <div>{_t("Sign out and remove encryption keys?")}</div>, description: <div>{_t("error|session_restore|clear_storage_description")}</div>,
button: _t("action|sign_out"), button: _t("action|sign_out"),
danger: true, danger: true,
onFinished: this.props.onFinished, onFinished: this.props.onFinished,
@ -59,7 +59,7 @@ export default class SessionRestoreErrorDialog extends React.Component<IProps> {
const clearStorageButton = ( const clearStorageButton = (
<button onClick={this.onClearStorageClick} className="danger"> <button onClick={this.onClearStorageClick} className="danger">
{_t("Clear Storage and Sign Out")} {_t("error|session_restore|clear_storage_button")}
</button> </button>
); );
@ -92,25 +92,16 @@ export default class SessionRestoreErrorDialog extends React.Component<IProps> {
<BaseDialog <BaseDialog
className="mx_ErrorDialog" className="mx_ErrorDialog"
onFinished={this.props.onFinished} onFinished={this.props.onFinished}
title={_t("Unable to restore session")} title={_t("error|session_restore|title")}
contentId="mx_Dialog_content" contentId="mx_Dialog_content"
hasCancel={false} hasCancel={false}
> >
<div className="mx_Dialog_content" id="mx_Dialog_content"> <div className="mx_Dialog_content" id="mx_Dialog_content">
<p>{_t("We encountered an error trying to restore your previous session.")}</p> <p>{_t("error|session_restore|description_1")}</p>
<p> <p>{_t("error|session_restore|description_2", { brand })}</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.",
{ brand },
)}
</p>
<p> <p>{_t("error|session_restore|description_3")}</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.",
)}
</p>
</div> </div>
{dialogButtons} {dialogButtons}
</BaseDialog> </BaseDialog>

View file

@ -76,10 +76,8 @@ export default class SetEmailDialog extends React.Component<IProps, IState> {
this.addThreepid.addEmailAddress(emailAddress).then( this.addThreepid.addEmailAddress(emailAddress).then(
() => { () => {
Modal.createDialog(QuestionDialog, { Modal.createDialog(QuestionDialog, {
title: _t("Verification Pending"), title: _t("auth|set_email|verification_pending_title"),
description: _t( description: _t("auth|set_email|verification_pending_description"),
"Please check your email and click on the link it contains. Once this is done, click continue.",
),
button: _t("action|continue"), button: _t("action|continue"),
onFinished: this.onEmailDialogFinished, onFinished: this.onEmailDialogFinished,
}); });
@ -125,11 +123,9 @@ export default class SetEmailDialog extends React.Component<IProps, IState> {
const message = const message =
_t("settings|general|error_email_verification") + _t("settings|general|error_email_verification") +
" " + " " +
_t( _t("auth|set_email|verification_pending_description");
"Please check your email and click on the link it contains. Once this is done, click continue.",
);
Modal.createDialog(QuestionDialog, { Modal.createDialog(QuestionDialog, {
title: _t("Verification Pending"), title: _t("auth|set_email|verification_pending_title"),
description: message, description: message,
button: _t("action|continue"), button: _t("action|continue"),
onFinished: this.onEmailDialogFinished, onFinished: this.onEmailDialogFinished,
@ -152,7 +148,7 @@ export default class SetEmailDialog extends React.Component<IProps, IState> {
<EditableText <EditableText
initialValue={this.state.emailAddress} initialValue={this.state.emailAddress}
className="mx_SetEmailDialog_email_input" className="mx_SetEmailDialog_email_input"
placeholder={_t("Email address")} placeholder={_t("common|email_address")}
placeholderClassName="mx_SetEmailDialog_email_input_placeholder" placeholderClassName="mx_SetEmailDialog_email_input_placeholder"
blurToCancel={false} blurToCancel={false}
onValueChanged={this.onEmailAddressChanged} onValueChanged={this.onEmailAddressChanged}
@ -167,9 +163,7 @@ export default class SetEmailDialog extends React.Component<IProps, IState> {
contentId="mx_Dialog_content" contentId="mx_Dialog_content"
> >
<div className="mx_Dialog_content"> <div className="mx_Dialog_content">
<p id="mx_Dialog_content"> <p id="mx_Dialog_content">{_t("auth|set_email|description")}</p>
{_t("This will allow you to reset your password and receive notifications.")}
</p>
{emailInput} {emailInput}
</div> </div>
<div className="mx_Dialog_buttons"> <div className="mx_Dialog_buttons">

View file

@ -130,7 +130,7 @@ export default class ShareDialog extends React.PureComponent<XOR<Props, EventPro
let checkbox: JSX.Element | undefined; let checkbox: JSX.Element | undefined;
if (this.props.target instanceof Room) { if (this.props.target instanceof Room) {
title = _t("Share Room"); title = _t("share|title_room");
const events = this.props.target.getLiveTimeline().getEvents(); const events = this.props.target.getLiveTimeline().getEvents();
if (events.length > 0) { if (events.length > 0) {
@ -140,22 +140,22 @@ export default class ShareDialog extends React.PureComponent<XOR<Props, EventPro
checked={this.state.linkSpecificEvent} checked={this.state.linkSpecificEvent}
onChange={this.onLinkSpecificEventCheckboxClick} onChange={this.onLinkSpecificEventCheckboxClick}
> >
{_t("Link to most recent message")} {_t("share|permalink_most_recent")}
</StyledCheckbox> </StyledCheckbox>
</div> </div>
); );
} }
} else if (this.props.target instanceof User || this.props.target instanceof RoomMember) { } else if (this.props.target instanceof User || this.props.target instanceof RoomMember) {
title = _t("Share User"); title = _t("share|title_user");
} else if (this.props.target instanceof MatrixEvent) { } else if (this.props.target instanceof MatrixEvent) {
title = _t("Share Room Message"); title = _t("share|title_message");
checkbox = ( checkbox = (
<div> <div>
<StyledCheckbox <StyledCheckbox
checked={this.state.linkSpecificEvent} checked={this.state.linkSpecificEvent}
onChange={this.onLinkSpecificEventCheckboxClick} onChange={this.onLinkSpecificEventCheckboxClick}
> >
{_t("Link to selected message")} {_t("share|permalink_message")}
</StyledCheckbox> </StyledCheckbox>
</div> </div>
); );
@ -208,7 +208,7 @@ export default class ShareDialog extends React.PureComponent<XOR<Props, EventPro
> >
<div className="mx_ShareDialog_content"> <div className="mx_ShareDialog_content">
<CopyableText getTextToCopy={() => matrixToUrl}> <CopyableText getTextToCopy={() => matrixToUrl}>
<a title={_t("Link to room")} href={matrixToUrl} onClick={ShareDialog.onLinkClick}> <a title={_t("share|link_title")} href={matrixToUrl} onClick={ShareDialog.onLinkClick}>
{matrixToUrl} {matrixToUrl}
</a> </a>
</CopyableText> </CopyableText>

View file

@ -64,7 +64,7 @@ const SlashCommandHelpDialog: React.FC<IProps> = ({ onFinished }) => {
return ( return (
<InfoDialog <InfoDialog
className="mx_SlashCommandHelpDialog" className="mx_SlashCommandHelpDialog"
title={_t("Command Help")} title={_t("slash_command|help_dialog_title")}
description={ description={
<table> <table>
<tbody>{body}</tbody> <tbody>{body}</tbody>

View file

@ -77,7 +77,7 @@ export const SlidingSyncOptionsDialog: React.FC<{ onFinished(enabled: boolean):
let nativeSupport: string; let nativeSupport: string;
if (hasNativeSupport === null) { if (hasNativeSupport === null) {
nativeSupport = _t("Checking…"); nativeSupport = _t("labs|sliding_sync_checking");
} else { } else {
nativeSupport = hasNativeSupport nativeSupport = hasNativeSupport
? _t("labs|sliding_sync_server_support") ? _t("labs|sliding_sync_server_support")
@ -103,7 +103,7 @@ export const SlidingSyncOptionsDialog: React.FC<{ onFinished(enabled: boolean):
key: "working", key: "working",
final: true, final: true,
test: async (_, { error }) => !error, test: async (_, { error }) => !error,
valid: () => _t("Looks good"), valid: () => _t("spotlight|public_rooms|network_dropdown_available_valid"),
invalid: ({ error }) => (error instanceof Error ? error.message : null), invalid: ({ error }) => (error instanceof Error ? error.message : null),
}, },
], ],

View file

@ -42,7 +42,7 @@ const SpacePreferencesAppearanceTab: React.FC<Pick<IProps, "space">> = ({ space
return ( return (
<SettingsTab> <SettingsTab>
<SettingsSection heading={_t("Sections to show")}> <SettingsSection heading={_t("space|preferences|sections_section")}>
<SettingsSubsection> <SettingsSubsection>
<StyledCheckbox <StyledCheckbox
checked={!!showPeople} checked={!!showPeople}
@ -58,12 +58,9 @@ const SpacePreferencesAppearanceTab: React.FC<Pick<IProps, "space">> = ({ space
{_t("common|people")} {_t("common|people")}
</StyledCheckbox> </StyledCheckbox>
<SettingsSubsectionText> <SettingsSubsectionText>
{_t( {_t("space|preferences|show_people_in_space", {
"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, spaceName: space.name,
}, })}
)}
</SettingsSubsectionText> </SettingsSubsectionText>
</SettingsSubsection> </SettingsSubsection>
</SettingsSection> </SettingsSection>

View file

@ -42,7 +42,7 @@ export default class StorageEvictedDialog extends React.Component<IProps> {
let logRequest; let logRequest;
if (SdkConfig.get().bug_report_endpoint_url) { if (SdkConfig.get().bug_report_endpoint_url) {
logRequest = _t( logRequest = _t(
"To help us prevent this in future, please <a>send us logs</a>.", "bug_reporting|log_request",
{}, {},
{ {
a: (text) => ( a: (text) => (
@ -58,18 +58,14 @@ export default class StorageEvictedDialog extends React.Component<IProps> {
<BaseDialog <BaseDialog
className="mx_ErrorDialog" className="mx_ErrorDialog"
onFinished={this.props.onFinished} onFinished={this.props.onFinished}
title={_t("Missing session data")} title={_t("error|storage_evicted_title")}
contentId="mx_Dialog_content" contentId="mx_Dialog_content"
hasCancel={false} hasCancel={false}
> >
<div className="mx_Dialog_content" id="mx_Dialog_content"> <div className="mx_Dialog_content" id="mx_Dialog_content">
<p>{_t("error|storage_evicted_description_1")}</p>
<p> <p>
{_t( {_t("error|storage_evicted_description_2")} {logRequest}
"Some session data, including encrypted message keys, is missing. Sign out and sign in to fix this, restoring keys from backup.",
)}
</p>
<p>
{_t("Your browser likely removed this data when running low on disk space.")} {logRequest}
</p> </p>
</div> </div>
<DialogButtons <DialogButtons

View file

@ -111,9 +111,9 @@ export default class TermsDialog extends React.PureComponent<ITermsDialogProps,
case SERVICE_TYPES.IS: case SERVICE_TYPES.IS:
return ( return (
<div> <div>
{_t("Find others by phone or email")} {_t("terms|summary_identity_server_1")}
<br /> <br />
{_t("Be found by phone or email")} {_t("terms|summary_identity_server_2")}
</div> </div>
); );
case SERVICE_TYPES.IM: case SERVICE_TYPES.IM:

View file

@ -35,14 +35,14 @@ const UntrustedDeviceDialog: React.FC<IProps> = ({ device, user, onFinished }) =
let newSessionText: string; let newSessionText: string;
if (MatrixClientPeg.safeGet().getUserId() === user.userId) { if (MatrixClientPeg.safeGet().getUserId() === user.userId) {
newSessionText = _t("You signed in to a new session without verifying it:"); newSessionText = _t("encryption|udd|own_new_session_text");
askToVerifyText = _t("Verify your other session using one of the options below."); askToVerifyText = _t("encryption|udd|own_ask_verify_text");
} else { } else {
newSessionText = _t("%(name)s (%(userId)s) signed in to a new session without verifying it:", { newSessionText = _t("encryption|udd|other_new_session_text", {
name: user.displayName, name: user.displayName,
userId: user.userId, userId: user.userId,
}); });
askToVerifyText = _t("Ask this user to verify their session, or manually verify it below."); askToVerifyText = _t("encryption|udd|other_ask_verify_text");
} }
return ( return (
@ -52,7 +52,7 @@ const UntrustedDeviceDialog: React.FC<IProps> = ({ device, user, onFinished }) =
title={ title={
<> <>
<E2EIcon status={E2EState.Warning} isUser size={24} hideTooltip={true} /> <E2EIcon status={E2EState.Warning} isUser size={24} hideTooltip={true} />
{_t("Not Trusted")} {_t("encryption|udd|title")}
</> </>
} }
> >
@ -65,10 +65,10 @@ const UntrustedDeviceDialog: React.FC<IProps> = ({ device, user, onFinished }) =
</div> </div>
<div className="mx_Dialog_buttons"> <div className="mx_Dialog_buttons">
<AccessibleButton kind="primary_outline" onClick={() => onFinished("legacy")}> <AccessibleButton kind="primary_outline" onClick={() => onFinished("legacy")}>
{_t("Manually verify by text")} {_t("encryption|udd|manual_verification_button")}
</AccessibleButton> </AccessibleButton>
<AccessibleButton kind="primary_outline" onClick={() => onFinished("sas")}> <AccessibleButton kind="primary_outline" onClick={() => onFinished("sas")}>
{_t("Interactively verify by emoji")} {_t("encryption|udd|interactive_verification_button")}
</AccessibleButton> </AccessibleButton>
<AccessibleButton kind="primary" onClick={() => onFinished(false)}> <AccessibleButton kind="primary" onClick={() => onFinished(false)}>
{_t("action|done")} {_t("action|done")}

View file

@ -69,12 +69,12 @@ export default class UploadConfirmDialog extends React.Component<IProps> {
public render(): React.ReactNode { public render(): React.ReactNode {
let title: string; let title: string;
if (this.props.totalFiles > 1 && this.props.currentIndex !== undefined) { if (this.props.totalFiles > 1 && this.props.currentIndex !== undefined) {
title = _t("Upload files (%(current)s of %(total)s)", { title = _t("upload_file|title_progress", {
current: this.props.currentIndex + 1, current: this.props.currentIndex + 1,
total: this.props.totalFiles, total: this.props.totalFiles,
}); });
} else { } else {
title = _t("Upload files"); title = _t("upload_file|title");
} }
const fileId = `mx-uploadconfirmdialog-${this.props.file.name}`; const fileId = `mx-uploadconfirmdialog-${this.props.file.name}`;
@ -99,7 +99,7 @@ export default class UploadConfirmDialog extends React.Component<IProps> {
let uploadAllButton: JSX.Element | undefined; let uploadAllButton: JSX.Element | undefined;
if (this.props.currentIndex + 1 < this.props.totalFiles) { if (this.props.currentIndex + 1 < this.props.totalFiles) {
uploadAllButton = <button onClick={this.onUploadAllClick}>{_t("Upload all")}</button>; uploadAllButton = <button onClick={this.onUploadAllClick}>{_t("upload_file|upload_all_button")}</button>;
} }
return ( return (

View file

@ -49,7 +49,7 @@ export default class UploadFailureDialog extends React.Component<IProps> {
let buttons; let buttons;
if (this.props.totalFiles === 1 && this.props.badFiles.length === 1) { if (this.props.totalFiles === 1 && this.props.badFiles.length === 1) {
message = _t( message = _t(
"This file is <b>too large</b> to upload. The file size limit is %(limit)s but this file is %(sizeOfThisFile)s.", "upload_file|error_file_too_large",
{ {
limit: fileSize(this.props.contentMessages.getUploadLimit()!), limit: fileSize(this.props.contentMessages.getUploadLimit()!),
sizeOfThisFile: fileSize(this.props.badFiles[0].size), sizeOfThisFile: fileSize(this.props.badFiles[0].size),
@ -68,7 +68,7 @@ export default class UploadFailureDialog extends React.Component<IProps> {
); );
} else if (this.props.totalFiles === this.props.badFiles.length) { } else if (this.props.totalFiles === this.props.badFiles.length) {
message = _t( message = _t(
"These files are <b>too large</b> to upload. The file size limit is %(limit)s.", "upload_file|error_files_too_large",
{ {
limit: fileSize(this.props.contentMessages.getUploadLimit()!), limit: fileSize(this.props.contentMessages.getUploadLimit()!),
}, },
@ -86,7 +86,7 @@ export default class UploadFailureDialog extends React.Component<IProps> {
); );
} else { } else {
message = _t( message = _t(
"Some files are <b>too large</b> to be uploaded. The file size limit is %(limit)s.", "upload_file|error_some_files_too_large",
{ {
limit: fileSize(this.props.contentMessages.getUploadLimit()!), limit: fileSize(this.props.contentMessages.getUploadLimit()!),
}, },
@ -97,10 +97,10 @@ export default class UploadFailureDialog extends React.Component<IProps> {
const howManyOthers = this.props.totalFiles - this.props.badFiles.length; const howManyOthers = this.props.totalFiles - this.props.badFiles.length;
buttons = ( buttons = (
<DialogButtons <DialogButtons
primaryButton={_t("Upload %(count)s other files", { count: howManyOthers })} primaryButton={_t("upload_file|upload_n_others_button", { count: howManyOthers })}
onPrimaryButtonClick={this.onUploadClick} onPrimaryButtonClick={this.onUploadClick}
hasCancel={true} hasCancel={true}
cancelButton={_t("Cancel All")} cancelButton={_t("upload_file|cancel_all_button")}
onCancel={this.onCancelClick} onCancel={this.onCancelClick}
focus={true} focus={true}
/> />
@ -111,7 +111,7 @@ export default class UploadFailureDialog extends React.Component<IProps> {
<BaseDialog <BaseDialog
className="mx_UploadFailureDialog" className="mx_UploadFailureDialog"
onFinished={this.onCancelClick} onFinished={this.onCancelClick}
title={_t("Upload Error")} title={_t("upload_file|error_title")}
contentId="mx_Dialog_content" contentId="mx_Dialog_content"
> >
<div id="mx_Dialog_content"> <div id="mx_Dialog_content">

View file

@ -49,7 +49,9 @@ export default class VerificationRequestDialog extends React.Component<IProps, I
const request = this.state.verificationRequest; const request = this.state.verificationRequest;
const otherUserId = request?.otherUserId; const otherUserId = request?.otherUserId;
const member = this.props.member || (otherUserId ? MatrixClientPeg.safeGet().getUser(otherUserId) : null); const member = this.props.member || (otherUserId ? MatrixClientPeg.safeGet().getUser(otherUserId) : null);
const title = request?.isSelfVerification ? _t("Verify other device") : _t("Verification Request"); const title = request?.isSelfVerification
? _t("encryption|verification|verification_dialog_title_device")
: _t("encryption|verification|verification_dialog_title_user");
if (!member) return null; if (!member) return null;

View file

@ -120,15 +120,15 @@ export default class WidgetCapabilitiesPromptDialog extends React.PureComponent<
<BaseDialog <BaseDialog
className="mx_WidgetCapabilitiesPromptDialog" className="mx_WidgetCapabilitiesPromptDialog"
onFinished={this.props.onFinished} onFinished={this.props.onFinished}
title={_t("Approve widget permissions")} title={_t("widget|capabilities_dialog|title")}
> >
<form onSubmit={this.onSubmit}> <form onSubmit={this.onSubmit}>
<div className="mx_Dialog_content"> <div className="mx_Dialog_content">
<div className="text-muted">{_t("This widget would like to:")}</div> <div className="text-muted">{_t("widget|capabilities_dialog|content_starting_text")}</div>
{checkboxRows} {checkboxRows}
<DialogButtons <DialogButtons
primaryButton={_t("action|approve")} primaryButton={_t("action|approve")}
cancelButton={_t("Decline All")} cancelButton={_t("widget|capabilities_dialog|decline_all_permission")}
onPrimaryButtonClick={this.onSubmit} onPrimaryButtonClick={this.onSubmit}
onCancel={this.onReject} onCancel={this.onReject}
additive={ additive={
@ -136,7 +136,7 @@ export default class WidgetCapabilitiesPromptDialog extends React.PureComponent<
value={this.state.rememberSelection} value={this.state.rememberSelection}
toggleInFront={true} toggleInFront={true}
onChange={this.onRememberSelectionChange} onChange={this.onRememberSelectionChange}
label={_t("Remember my selection for this widget")} label={_t("widget|capabilities_dialog|remember_Selection")}
/> />
} }
/> />

View file

@ -79,10 +79,10 @@ export default class WidgetOpenIDPermissionsDialog extends React.PureComponent<I
className="mx_WidgetOpenIDPermissionsDialog" className="mx_WidgetOpenIDPermissionsDialog"
hasCancel={true} hasCancel={true}
onFinished={this.props.onFinished} onFinished={this.props.onFinished}
title={_t("Allow this widget to verify your identity")} title={_t("widget|open_id_permissions_dialog|title")}
> >
<div className="mx_WidgetOpenIDPermissionsDialog_content"> <div className="mx_WidgetOpenIDPermissionsDialog_content">
<p>{_t("The widget will verify your user ID, but won't be able to perform actions for you:")}</p> <p>{_t("widget|open_id_permissions_dialog|starting_text")}</p>
<p className="text-muted"> <p className="text-muted">
{/* cheap trim to just get the path */} {/* cheap trim to just get the path */}
{this.props.widget.templateUrl.split("?")[0].split("#")[0]} {this.props.widget.templateUrl.split("?")[0].split("#")[0]}
@ -97,7 +97,7 @@ export default class WidgetOpenIDPermissionsDialog extends React.PureComponent<I
value={this.state.rememberSelection} value={this.state.rememberSelection}
toggleInFront={true} toggleInFront={true}
onChange={this.onRememberSelectionChange} onChange={this.onRememberSelectionChange}
label={_t("Remember this")} label={_t("widget|open_id_permissions_dialog|remember_selection")}
/> />
} }
/> />

View file

@ -117,7 +117,7 @@ export const EventEditor: React.FC<IEventEditorProps> = ({ fieldDefs, defaultCon
}; };
return ( return (
<BaseTool actionLabel={_t("Send")} onAction={onAction} onBack={onBack}> <BaseTool actionLabel={_t("forward|send_label")} onAction={onAction} onBack={onBack}>
<div className="mx_DevTools_eventTypeStateKeyGroup">{fields}</div> <div className="mx_DevTools_eventTypeStateKeyGroup">{fields}</div>
<Field <Field

View file

@ -59,7 +59,7 @@ const FilteredList: React.FC<IProps> = ({ children, query, onChange }) => {
return ( return (
<button className="mx_DevTools_button" onClick={showMore}> <button className="mx_DevTools_button" onClick={showMore}>
{_t("and %(count)s others...", { count: overflowCount })} {_t("common|and_n_others", { count: overflowCount })}
</button> </button>
); );
}; };
@ -67,7 +67,7 @@ const FilteredList: React.FC<IProps> = ({ children, query, onChange }) => {
return ( return (
<> <>
<Field <Field
label={_t("Filter results")} label={_t("common|filter_results")}
autoFocus={true} autoFocus={true}
size={64} size={64}
type="text" type="text"
@ -80,7 +80,7 @@ const FilteredList: React.FC<IProps> = ({ children, query, onChange }) => {
/> />
{filteredChildren.length < 1 ? ( {filteredChildren.length < 1 ? (
_t("No results found") _t("common|no_results_found")
) : ( ) : (
<TruncatedList <TruncatedList
getChildren={getChildren} getChildren={getChildren}

View file

@ -277,7 +277,7 @@ const SettingsList: React.FC<ISettingsListProps> = ({ onBack, onView, onEdit })
return ( return (
<BaseTool onBack={onBack} className="mx_DevTools_SettingsExplorer"> <BaseTool onBack={onBack} className="mx_DevTools_SettingsExplorer">
<Field <Field
label={_t("Filter results")} label={_t("common|filter_results")}
autoFocus={true} autoFocus={true}
size={64} size={64}
type="text" type="text"

View file

@ -27,7 +27,7 @@ import BaseTool, { DevtoolsContext, IDevtoolsProps } from "./BaseTool";
import { Tool } from "../DevtoolsDialog"; import { Tool } from "../DevtoolsDialog";
const PHASE_MAP: Record<Phase, TranslationKey> = { const PHASE_MAP: Record<Phase, TranslationKey> = {
[Phase.Unsent]: _td("Unsent"), [Phase.Unsent]: _td("common|unsent"),
[Phase.Requested]: _td("devtools|phase_requested"), [Phase.Requested]: _td("devtools|phase_requested"),
[Phase.Ready]: _td("devtools|phase_ready"), [Phase.Ready]: _td("devtools|phase_ready"),
[Phase.Done]: _td("action|done"), [Phase.Done]: _td("action|done"),

View file

@ -45,7 +45,7 @@ export const OidcLogoutDialog: React.FC<OidcLogoutDialogProps> = ({
return ( return (
<BaseDialog onFinished={onFinished} title={_t("action|sign_out")} contentId="mx_Dialog_content"> <BaseDialog onFinished={onFinished} title={_t("action|sign_out")} contentId="mx_Dialog_content">
<div className="mx_Dialog_content" id="mx_Dialog_content"> <div className="mx_Dialog_content" id="mx_Dialog_content">
{_t("You will be redirected to your server's authentication provider to complete sign out.")} {_t("auth|oidc|logout_redirect_warning")}
</div> </div>
<div className="mx_Dialog_buttons"> <div className="mx_Dialog_buttons">
{hasOpenedLogoutLink ? ( {hasOpenedLogoutLink ? (

View file

@ -263,15 +263,15 @@ export default class AccessSecretStorageDialog extends React.PureComponent<IProp
private getKeyValidationText(): string { private getKeyValidationText(): string {
if (this.state.recoveryKeyFileError) { if (this.state.recoveryKeyFileError) {
return _t("Wrong file type"); return _t("encryption|access_secret_storage_dialog|key_validation_text|wrong_file_type");
} else if (this.state.recoveryKeyCorrect) { } else if (this.state.recoveryKeyCorrect) {
return _t("Looks good!"); return _t("encryption|access_secret_storage_dialog|key_validation_text|recovery_key_is_correct");
} else if (this.state.recoveryKeyValid) { } else if (this.state.recoveryKeyValid) {
return _t("Wrong Security Key"); return _t("encryption|access_secret_storage_dialog|key_validation_text|wrong_security_key");
} else if (this.state.recoveryKeyValid === null) { } else if (this.state.recoveryKeyValid === null) {
return ""; return "";
} else { } else {
return _t("Invalid Security Key"); return _t("encryption|access_secret_storage_dialog|key_validation_text|invalid_security_key");
} }
} }
@ -280,7 +280,7 @@ export default class AccessSecretStorageDialog extends React.PureComponent<IProp
const resetButton = ( const resetButton = (
<div className="mx_AccessSecretStorageDialog_reset"> <div className="mx_AccessSecretStorageDialog_reset">
{_t("Forgotten or lost all recovery methods? <a>Reset all</a>", undefined, { {_t("encryption|reset_all_button", undefined, {
a: (sub) => ( a: (sub) => (
<AccessibleButton <AccessibleButton
kind="link_inline" kind="link_inline"
@ -298,16 +298,12 @@ export default class AccessSecretStorageDialog extends React.PureComponent<IProp
let title; let title;
let titleClass; let titleClass;
if (this.state.resetting) { if (this.state.resetting) {
title = _t("Reset everything"); title = _t("encryption|access_secret_storage_dialog|reset_title");
titleClass = ["mx_AccessSecretStorageDialog_titleWithIcon mx_AccessSecretStorageDialog_resetBadge"]; titleClass = ["mx_AccessSecretStorageDialog_titleWithIcon mx_AccessSecretStorageDialog_resetBadge"];
content = ( content = (
<div> <div>
<p>{_t("Only do this if you have no other device to complete verification with.")}</p> <p>{_t("encryption|access_secret_storage_dialog|reset_warning_1")}</p>
<p> <p>{_t("encryption|access_secret_storage_dialog|reset_warning_2")}</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.",
)}
</p>
<DialogButtons <DialogButtons
primaryButton={_t("action|reset")} primaryButton={_t("action|reset")}
onPrimaryButtonClick={this.onConfirmResetAllClick} onPrimaryButtonClick={this.onConfirmResetAllClick}
@ -319,7 +315,7 @@ export default class AccessSecretStorageDialog extends React.PureComponent<IProp
</div> </div>
); );
} else if (hasPassphrase && !this.state.forceRecoveryKey) { } else if (hasPassphrase && !this.state.forceRecoveryKey) {
title = _t("Security Phrase"); title = _t("encryption|access_secret_storage_dialog|security_phrase_title");
titleClass = ["mx_AccessSecretStorageDialog_titleWithIcon mx_AccessSecretStorageDialog_securePhraseTitle"]; titleClass = ["mx_AccessSecretStorageDialog_titleWithIcon mx_AccessSecretStorageDialog_securePhraseTitle"];
let keyStatus; let keyStatus;
@ -327,9 +323,7 @@ export default class AccessSecretStorageDialog extends React.PureComponent<IProp
keyStatus = ( keyStatus = (
<div className="mx_AccessSecretStorageDialog_keyStatus"> <div className="mx_AccessSecretStorageDialog_keyStatus">
{"\uD83D\uDC4E "} {"\uD83D\uDC4E "}
{_t( {_t("encryption|access_secret_storage_dialog|security_phrase_incorrect_error")}
"Unable to access secret storage. Please verify that you entered the correct Security Phrase.",
)}
</div> </div>
); );
} else { } else {
@ -340,7 +334,7 @@ export default class AccessSecretStorageDialog extends React.PureComponent<IProp
<div> <div>
<p> <p>
{_t( {_t(
"Enter your Security Phrase or <button>use your Security Key</button> to continue.", "encryption|access_secret_storage_dialog|enter_phrase_or_key_prompt",
{}, {},
{ {
button: (s) => ( button: (s) => (
@ -358,7 +352,7 @@ export default class AccessSecretStorageDialog extends React.PureComponent<IProp
id="mx_passPhraseInput" id="mx_passPhraseInput"
className="mx_AccessSecretStorageDialog_passPhraseInput" className="mx_AccessSecretStorageDialog_passPhraseInput"
type="password" type="password"
label={_t("Security Phrase")} label={_t("encryption|access_secret_storage_dialog|security_phrase_title")}
value={this.state.passPhrase} value={this.state.passPhrase}
onChange={this.onPassPhraseChange} onChange={this.onPassPhraseChange}
autoFocus={true} autoFocus={true}
@ -378,7 +372,7 @@ export default class AccessSecretStorageDialog extends React.PureComponent<IProp
</div> </div>
); );
} else { } else {
title = _t("Security Key"); title = _t("encryption|access_secret_storage_dialog|security_key_title");
titleClass = ["mx_AccessSecretStorageDialog_titleWithIcon mx_AccessSecretStorageDialog_secureBackupTitle"]; titleClass = ["mx_AccessSecretStorageDialog_titleWithIcon mx_AccessSecretStorageDialog_secureBackupTitle"];
const feedbackClasses = classNames({ const feedbackClasses = classNames({
@ -390,7 +384,7 @@ export default class AccessSecretStorageDialog extends React.PureComponent<IProp
content = ( content = (
<div> <div>
<p>{_t("Use your Security Key to continue.")}</p> <p>{_t("encryption|access_secret_storage_dialog|use_security_key_prompt")}</p>
<form <form
className="mx_AccessSecretStorageDialog_primaryContainer" className="mx_AccessSecretStorageDialog_primaryContainer"
@ -403,7 +397,7 @@ export default class AccessSecretStorageDialog extends React.PureComponent<IProp
<Field <Field
type="password" type="password"
id="mx_securityKey" id="mx_securityKey"
label={_t("Security Key")} label={_t("encryption|access_secret_storage_dialog|security_key_title")}
value={this.state.recoveryKey} value={this.state.recoveryKey}
onChange={this.onRecoveryKeyChange} onChange={this.onRecoveryKeyChange}
autoFocus={true} autoFocus={true}
@ -412,7 +406,7 @@ export default class AccessSecretStorageDialog extends React.PureComponent<IProp
/> />
</div> </div>
<span className="mx_AccessSecretStorageDialog_recoveryKeyEntry_entryControlSeparatorText"> <span className="mx_AccessSecretStorageDialog_recoveryKeyEntry_entryControlSeparatorText">
{_t("%(securityKey)s or %(recoveryFile)s", { {_t("encryption|access_secret_storage_dialog|separator", {
recoveryFile: "", recoveryFile: "",
securityKey: "", securityKey: "",
})} })}

View file

@ -39,17 +39,13 @@ export default class ConfirmDestroyCrossSigningDialog extends React.Component<IP
className="mx_ConfirmDestroyCrossSigningDialog" className="mx_ConfirmDestroyCrossSigningDialog"
hasCancel={true} hasCancel={true}
onFinished={this.props.onFinished} onFinished={this.props.onFinished}
title={_t("Destroy cross-signing keys?")} title={_t("encryption|destroy_cross_signing_dialog|title")}
> >
<div className="mx_ConfirmDestroyCrossSigningDialog_content"> <div className="mx_ConfirmDestroyCrossSigningDialog_content">
<p> <p>{_t("encryption|destroy_cross_signing_dialog|warning")}</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.",
)}
</p>
</div> </div>
<DialogButtons <DialogButtons
primaryButton={_t("Clear cross-signing keys")} primaryButton={_t("encryption|destroy_cross_signing_dialog|primary_button_text")}
onPrimaryButtonClick={this.onConfirm} onPrimaryButtonClick={this.onConfirm}
primaryButtonClass="danger" primaryButtonClass="danger"
cancelButton={_t("action|cancel")} cancelButton={_t("action|cancel")}

View file

@ -113,13 +113,13 @@ export default class CreateCrossSigningDialog extends React.PureComponent<IProps
const dialogAesthetics = { const dialogAesthetics = {
[SSOAuthEntry.PHASE_PREAUTH]: { [SSOAuthEntry.PHASE_PREAUTH]: {
title: _t("auth|uia|sso_title"), title: _t("auth|uia|sso_title"),
body: _t("To continue, use Single Sign On to prove your identity."), body: _t("auth|uia|sso_preauth_body"),
continueText: _t("auth|sso"), continueText: _t("auth|sso"),
continueKind: "primary", continueKind: "primary",
}, },
[SSOAuthEntry.PHASE_POSTAUTH]: { [SSOAuthEntry.PHASE_POSTAUTH]: {
title: _t("Confirm encryption setup"), title: _t("encryption|confirm_encryption_setup_title"),
body: _t("Click the button below to confirm setting up encryption."), body: _t("encryption|confirm_encryption_setup_body"),
continueText: _t("action|confirm"), continueText: _t("action|confirm"),
continueKind: "primary", continueKind: "primary",
}, },
@ -173,7 +173,7 @@ export default class CreateCrossSigningDialog extends React.PureComponent<IProps
if (this.state.error) { if (this.state.error) {
content = ( content = (
<div> <div>
<p>{_t("Unable to set up keys")}</p> <p>{_t("encryption|unable_to_setup_keys_error")}</p>
<div className="mx_Dialog_buttons"> <div className="mx_Dialog_buttons">
<DialogButtons <DialogButtons
primaryButton={_t("action|retry")} primaryButton={_t("action|retry")}

View file

@ -318,18 +318,18 @@ export default class RestoreKeyBackupDialog extends React.PureComponent<IProps,
let content; let content;
let title; let title;
if (this.state.loading) { if (this.state.loading) {
title = _t("Restoring keys from backup"); title = _t("encryption|access_secret_storage_dialog|restoring");
let details; let details;
if (this.state.progress.stage === ProgressState.Fetch) { if (this.state.progress.stage === ProgressState.Fetch) {
details = _t("Fetching keys from server…"); details = _t("restore_key_backup_dialog|key_fetch_in_progress");
} else if (this.state.progress.stage === ProgressState.LoadKeys) { } else if (this.state.progress.stage === ProgressState.LoadKeys) {
const { total, successes, failures } = this.state.progress; const { total, successes, failures } = this.state.progress;
details = _t("%(completed)s of %(total)s keys restored", { details = _t("restore_key_backup_dialog|load_keys_progress", {
total, total,
completed: (successes ?? 0) + (failures ?? 0), completed: (successes ?? 0) + (failures ?? 0),
}); });
} else if (this.state.progress.stage === ProgressState.PreFetch) { } else if (this.state.progress.stage === ProgressState.PreFetch) {
details = _t("Fetching keys from server…"); details = _t("restore_key_backup_dialog|key_fetch_in_progress");
} }
content = ( content = (
<div> <div>
@ -339,49 +339,41 @@ export default class RestoreKeyBackupDialog extends React.PureComponent<IProps,
); );
} else if (this.state.loadError) { } else if (this.state.loadError) {
title = _t("common|error"); title = _t("common|error");
content = _t("Unable to load backup status"); content = _t("restore_key_backup_dialog|load_error_content");
} else if (this.state.restoreError) { } else if (this.state.restoreError) {
if ( if (
this.state.restoreError instanceof MatrixError && this.state.restoreError instanceof MatrixError &&
this.state.restoreError.errcode === MatrixClient.RESTORE_BACKUP_ERROR_BAD_KEY this.state.restoreError.errcode === MatrixClient.RESTORE_BACKUP_ERROR_BAD_KEY
) { ) {
if (this.state.restoreType === RestoreType.RecoveryKey) { if (this.state.restoreType === RestoreType.RecoveryKey) {
title = _t("Security Key mismatch"); title = _t("restore_key_backup_dialog|recovery_key_mismatch_title");
content = ( content = (
<div> <div>
<p> <p>{_t("restore_key_backup_dialog|recovery_key_mismatch_description")}</p>
{_t(
"Backup could not be decrypted with this Security Key: please verify that you entered the correct Security Key.",
)}
</p>
</div> </div>
); );
} else { } else {
title = _t("Incorrect Security Phrase"); title = _t("restore_key_backup_dialog|incorrect_security_phrase_title");
content = ( content = (
<div> <div>
<p> <p>{_t("restore_key_backup_dialog|incorrect_security_phrase_dialog")}</p>
{_t(
"Backup could not be decrypted with this Security Phrase: please verify that you entered the correct Security Phrase.",
)}
</p>
</div> </div>
); );
} }
} else { } else {
title = _t("common|error"); title = _t("common|error");
content = _t("Unable to restore backup"); content = _t("restore_key_backup_dialog|restore_failed_error");
} }
} else if (this.state.backupInfo === null) { } else if (this.state.backupInfo === null) {
title = _t("common|error"); title = _t("common|error");
content = _t("No backup found!"); content = _t("restore_key_backup_dialog|no_backup_error");
} else if (this.state.recoverInfo) { } else if (this.state.recoverInfo) {
title = _t("Keys restored"); title = _t("restore_key_backup_dialog|keys_restored_title");
let failedToDecrypt; let failedToDecrypt;
if (this.state.recoverInfo.total > this.state.recoverInfo.imported) { if (this.state.recoverInfo.total > this.state.recoverInfo.imported) {
failedToDecrypt = ( failedToDecrypt = (
<p> <p>
{_t("Failed to decrypt %(failedCount)s sessions!", { {_t("restore_key_backup_dialog|count_of_decryption_failures", {
failedCount: this.state.recoverInfo.total - this.state.recoverInfo.imported, failedCount: this.state.recoverInfo.total - this.state.recoverInfo.imported,
})} })}
</p> </p>
@ -390,7 +382,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent<IProps,
content = ( content = (
<div> <div>
<p> <p>
{_t("Successfully restored %(sessionCount)s keys", { {_t("restore_key_backup_dialog|count_of_successfully_restored_keys", {
sessionCount: this.state.recoverInfo.imported, sessionCount: this.state.recoverInfo.imported,
})} })}
</p> </p>
@ -404,21 +396,11 @@ export default class RestoreKeyBackupDialog extends React.PureComponent<IProps,
</div> </div>
); );
} else if (backupHasPassphrase && !this.state.forceRecoveryKey) { } else if (backupHasPassphrase && !this.state.forceRecoveryKey) {
title = _t("Enter Security Phrase"); title = _t("restore_key_backup_dialog|enter_phrase_title");
content = ( content = (
<div> <div>
<p> <p>{_t("restore_key_backup_dialog|key_backup_warning", {}, { b: (sub) => <b>{sub}</b> })}</p>
{_t( <p>{_t("restore_key_backup_dialog|enter_phrase_description")}</p>
"<b>Warning</b>: you should only set up key backup from a trusted computer.",
{},
{ b: (sub) => <b>{sub}</b> },
)}
</p>
<p>
{_t(
"Access your secure message history and set up secure messaging by entering your Security Phrase.",
)}
</p>
<form className="mx_RestoreKeyBackupDialog_primaryContainer"> <form className="mx_RestoreKeyBackupDialog_primaryContainer">
<input <input
@ -438,7 +420,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent<IProps,
/> />
</form> </form>
{_t( {_t(
"If you've forgotten your Security Phrase you can <button1>use your Security Key</button1> or <button2>set up new recovery options</button2>", "restore_key_backup_dialog|phrase_forgotten_text",
{}, {},
{ {
button1: (s) => ( button1: (s) => (
@ -456,7 +438,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent<IProps,
</div> </div>
); );
} else { } else {
title = _t("Enter Security Key"); title = _t("restore_key_backup_dialog|enter_key_title");
let keyStatus; let keyStatus;
if (this.state.recoveryKey.length === 0) { if (this.state.recoveryKey.length === 0) {
@ -465,32 +447,22 @@ export default class RestoreKeyBackupDialog extends React.PureComponent<IProps,
keyStatus = ( keyStatus = (
<div className="mx_RestoreKeyBackupDialog_keyStatus"> <div className="mx_RestoreKeyBackupDialog_keyStatus">
{"\uD83D\uDC4D "} {"\uD83D\uDC4D "}
{_t("This looks like a valid Security Key!")} {_t("restore_key_backup_dialog|key_is_valid")}
</div> </div>
); );
} else { } else {
keyStatus = ( keyStatus = (
<div className="mx_RestoreKeyBackupDialog_keyStatus"> <div className="mx_RestoreKeyBackupDialog_keyStatus">
{"\uD83D\uDC4E "} {"\uD83D\uDC4E "}
{_t("Not a valid Security Key")} {_t("restore_key_backup_dialog|key_is_invalid")}
</div> </div>
); );
} }
content = ( content = (
<div> <div>
<p> <p>{_t("restore_key_backup_dialog|key_backup_warning", {}, { b: (sub) => <b>{sub}</b> })}</p>
{_t( <p>{_t("restore_key_backup_dialog|enter_key_description")}</p>
"<b>Warning</b>: you should only set up key backup from a trusted computer.",
{},
{ b: (sub) => <b>{sub}</b> },
)}
</p>
<p>
{_t(
"Access your secure message history and set up secure messaging by entering your Security Key.",
)}
</p>
<div className="mx_RestoreKeyBackupDialog_primaryContainer"> <div className="mx_RestoreKeyBackupDialog_primaryContainer">
<input <input
@ -510,7 +482,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent<IProps,
/> />
</div> </div>
{_t( {_t(
"If you've forgotten your Security Key you can <button>set up new recovery options</button>", "restore_key_backup_dialog|key_forgotten_text",
{}, {},
{ {
button: (s) => ( button: (s) => (

View file

@ -60,7 +60,7 @@ export function PublicRoomResultDetails({ room, labelId, descriptionId, detailsI
</div> </div>
<div id={detailsId} className="mx_SpotlightDialog_result_publicRoomDescription"> <div id={detailsId} className="mx_SpotlightDialog_result_publicRoomDescription">
<span className="mx_SpotlightDialog_result_publicRoomMemberCount"> <span className="mx_SpotlightDialog_result_publicRoomMemberCount">
{_t("%(count)s Members", { {_t("spotlight_dialog|count_of_members", {
count: room.num_joined_members, count: room.num_joined_members,
})} })}
</span> </span>

View file

@ -123,9 +123,9 @@ function filterToLabel(filter: Filter): string {
case Filter.People: case Filter.People:
return _t("common|people"); return _t("common|people");
case Filter.PublicRooms: case Filter.PublicRooms:
return _t("Public rooms"); return _t("spotlight_dialog|public_rooms_label");
case Filter.PublicSpaces: case Filter.PublicSpaces:
return _t("Public spaces"); return _t("spotlight_dialog|public_spaces_label");
} }
} }
@ -571,7 +571,9 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
aria-labelledby="mx_SpotlightDialog_section_otherSearches" aria-labelledby="mx_SpotlightDialog_section_otherSearches"
> >
<h4 id="mx_SpotlightDialog_section_otherSearches"> <h4 id="mx_SpotlightDialog_section_otherSearches">
{trimmedQuery ? _t('Use "%(query)s" to search', { query }) : _t("Search for")} {trimmedQuery
? _t("spotlight_dialog|heading_with_query", { query })
: _t("spotlight_dialog|heading_without_query")}
</h4> </h4>
<div> <div>
{filter !== Filter.PublicSpaces && supportsSpaceFiltering && ( {filter !== Filter.PublicSpaces && supportsSpaceFiltering && (
@ -760,7 +762,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
role="group" role="group"
aria-labelledby="mx_SpotlightDialog_section_people" aria-labelledby="mx_SpotlightDialog_section_people"
> >
<h4 id="mx_SpotlightDialog_section_people">{_t("Recent Conversations")}</h4> <h4 id="mx_SpotlightDialog_section_people">{_t("invite|recents_section")}</h4>
<div>{results[Section.People].slice(0, SECTION_LIMIT).map(resultMapper)}</div> <div>{results[Section.People].slice(0, SECTION_LIMIT).map(resultMapper)}</div>
</div> </div>
); );
@ -802,7 +804,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
role="group" role="group"
aria-labelledby="mx_SpotlightDialog_section_spaces" aria-labelledby="mx_SpotlightDialog_section_spaces"
> >
<h4 id="mx_SpotlightDialog_section_spaces">{_t("Spaces you're in")}</h4> <h4 id="mx_SpotlightDialog_section_spaces">{_t("spotlight_dialog|spaces_title")}</h4>
<div>{results[Section.Spaces].slice(0, SECTION_LIMIT).map(resultMapper)}</div> <div>{results[Section.Spaces].slice(0, SECTION_LIMIT).map(resultMapper)}</div>
</div> </div>
); );
@ -815,8 +817,8 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
content = ( content = (
<div className="mx_SpotlightDialog_otherSearches_messageSearchText"> <div className="mx_SpotlightDialog_otherSearches_messageSearchText">
{filter === Filter.PublicRooms {filter === Filter.PublicRooms
? _t("Failed to query public rooms") ? _t("spotlight_dialog|failed_querying_public_rooms")
: _t("Failed to query public spaces")} : _t("spotlight_dialog|failed_querying_public_spaces")}
</div> </div>
); );
} else { } else {
@ -849,7 +851,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
aria-labelledby="mx_SpotlightDialog_section_spaceRooms" aria-labelledby="mx_SpotlightDialog_section_spaceRooms"
> >
<h4 id="mx_SpotlightDialog_section_spaceRooms"> <h4 id="mx_SpotlightDialog_section_spaceRooms">
{_t("Other rooms in %(spaceName)s", { spaceName: activeSpace.name })} {_t("spotlight_dialog|other_rooms_in_space", { spaceName: activeSpace.name })}
</h4> </h4>
<div> <div>
{spaceResults.slice(0, SECTION_LIMIT).map( {spaceResults.slice(0, SECTION_LIMIT).map(
@ -909,7 +911,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
onFinished(); onFinished();
}} }}
> >
{_t("Join %(roomAddress)s", { {_t("spotlight_dialog|join_button_text", {
roomAddress: trimmedQuery, roomAddress: trimmedQuery,
})} })}
</Option> </Option>
@ -922,9 +924,9 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
if (filter === Filter.People) { if (filter === Filter.People) {
hiddenResultsSection = ( hiddenResultsSection = (
<div className="mx_SpotlightDialog_section mx_SpotlightDialog_hiddenResults" role="group"> <div className="mx_SpotlightDialog_section mx_SpotlightDialog_hiddenResults" role="group">
<h4>{_t("Some results may be hidden for privacy")}</h4> <h4>{_t("spotlight_dialog|result_may_be_hidden_privacy_warning")}</h4>
<div className="mx_SpotlightDialog_otherSearches_messageSearchText"> <div className="mx_SpotlightDialog_otherSearches_messageSearchText">
{_t("If you can't see who you're looking for, send them your invite link.")} {_t("spotlight_dialog|cant_find_person_helpful_hint")}
</div> </div>
<TooltipOption <TooltipOption
id="mx_SpotlightDialog_button_inviteLink" id="mx_SpotlightDialog_button_inviteLink"
@ -937,7 +939,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
title={inviteLinkCopied ? _t("common|copied") : _t("action|copy")} title={inviteLinkCopied ? _t("common|copied") : _t("action|copy")}
> >
<span className="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary_outline"> <span className="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary_outline">
{_t("Copy invite link")} {_t("spotlight_dialog|copy_link_text")}
</span> </span>
</TooltipOption> </TooltipOption>
</div> </div>
@ -945,9 +947,9 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
} else if (trimmedQuery && (filter === Filter.PublicRooms || filter === Filter.PublicSpaces)) { } else if (trimmedQuery && (filter === Filter.PublicRooms || filter === Filter.PublicSpaces)) {
hiddenResultsSection = ( hiddenResultsSection = (
<div className="mx_SpotlightDialog_section mx_SpotlightDialog_hiddenResults" role="group"> <div className="mx_SpotlightDialog_section mx_SpotlightDialog_hiddenResults" role="group">
<h4>{_t("Some results may be hidden")}</h4> <h4>{_t("spotlight_dialog|result_may_be_hidden_warning")}</h4>
<div className="mx_SpotlightDialog_otherSearches_messageSearchText"> <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("spotlight_dialog|cant_find_room_helpful_hint")}
</div> </div>
<Option <Option
id="mx_SpotlightDialog_button_createNewRoom" id="mx_SpotlightDialog_button_createNewRoom"
@ -961,7 +963,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
} }
> >
<span className="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary_outline"> <span className="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary_outline">
{_t("Create new room")} {_t("spotlight_dialog|create_new_room_button")}
</span> </span>
</Option> </Option>
</div> </div>
@ -976,13 +978,13 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
role="group" role="group"
aria-labelledby="mx_SpotlightDialog_section_groupChat" aria-labelledby="mx_SpotlightDialog_section_groupChat"
> >
<h4 id="mx_SpotlightDialog_section_groupChat">{_t("Other options")}</h4> <h4 id="mx_SpotlightDialog_section_groupChat">{_t("spotlight_dialog|group_chat_section_title")}</h4>
<Option <Option
id="mx_SpotlightDialog_button_startGroupChat" id="mx_SpotlightDialog_button_startGroupChat"
className="mx_SpotlightDialog_startGroupChat" className="mx_SpotlightDialog_startGroupChat"
onClick={() => showStartChatInviteDialog(trimmedQuery)} onClick={() => showStartChatInviteDialog(trimmedQuery)}
> >
{_t("Start a group chat")} {_t("spotlight_dialog|start_group_chat_button")}
</Option> </Option>
</div> </div>
); );
@ -996,10 +998,12 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
role="group" role="group"
aria-labelledby="mx_SpotlightDialog_section_messageSearch" aria-labelledby="mx_SpotlightDialog_section_messageSearch"
> >
<h4 id="mx_SpotlightDialog_section_messageSearch">{_t("Other searches")}</h4> <h4 id="mx_SpotlightDialog_section_messageSearch">
{_t("spotlight_dialog|message_search_section_title")}
</h4>
<div className="mx_SpotlightDialog_otherSearches_messageSearchText"> <div className="mx_SpotlightDialog_otherSearches_messageSearchText">
{_t( {_t(
"To search messages, look for this icon at the top of a room <icon/>", "spotlight_dialog|search_messages_hint",
{}, {},
{ icon: () => <div className="mx_SpotlightDialog_otherSearches_messageSearchIcon" /> }, { icon: () => <div className="mx_SpotlightDialog_otherSearches_messageSearchIcon" /> },
)} )}
@ -1036,7 +1040,9 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
aria-labelledby="mx_SpotlightDialog_section_recentSearches" aria-labelledby="mx_SpotlightDialog_section_recentSearches"
> >
<h4> <h4>
<span id="mx_SpotlightDialog_section_recentSearches">{_t("Recent searches")}</span> <span id="mx_SpotlightDialog_section_recentSearches">
{_t("spotlight_dialog|recent_searches_section_title")}
</span>
<AccessibleButton kind="link" onClick={clearRecentSearches}> <AccessibleButton kind="link" onClick={clearRecentSearches}>
{_t("action|clear")} {_t("action|clear")}
</AccessibleButton> </AccessibleButton>
@ -1086,7 +1092,9 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
role="group" role="group"
aria-labelledby="mx_SpotlightDialog_section_recentlyViewed" aria-labelledby="mx_SpotlightDialog_section_recentlyViewed"
> >
<h4 id="mx_SpotlightDialog_section_recentlyViewed">{_t("Recently viewed")}</h4> <h4 id="mx_SpotlightDialog_section_recentlyViewed">
{_t("spotlight_dialog|recently_viewed_section_title")}
</h4>
<div> <div>
{BreadcrumbsStore.instance.rooms {BreadcrumbsStore.instance.rooms
.filter((r) => r.roomId !== SdkContextClass.instance.roomViewStore.getRoomId()) .filter((r) => r.roomId !== SdkContextClass.instance.roomViewStore.getRoomId())
@ -1209,7 +1217,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
<> <>
<div id="mx_SpotlightDialog_keyboardPrompt"> <div id="mx_SpotlightDialog_keyboardPrompt">
{_t( {_t(
"Use <arrows/> to scroll", "spotlight_dialog|keyboard_scroll_hint",
{}, {},
{ {
arrows: () => ( arrows: () => (
@ -1230,7 +1238,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
hasCancel={false} hasCancel={false}
onKeyDown={onDialogKeyDown} onKeyDown={onDialogKeyDown}
screenName="UnifiedSearch" screenName="UnifiedSearch"
aria-label={_t("Search Dialog")} aria-label={_t("spotlight_dialog|search_dialog")}
> >
<div className="mx_SpotlightDialog_searchBox mx_textinput"> <div className="mx_SpotlightDialog_searchBox mx_textinput">
{filter !== null && ( {filter !== null && (
@ -1244,7 +1252,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
<span>{filterToLabel(filter)}</span> <span>{filterToLabel(filter)}</span>
<AccessibleButton <AccessibleButton
tabIndex={-1} tabIndex={-1}
alt={_t("Remove search filter for %(filter)s", { alt={_t("spotlight_dialog|remove_filter", {
filter: filterToLabel(filter), filter: filterToLabel(filter),
})} })}
className="mx_SpotlightDialog_filter--close" className="mx_SpotlightDialog_filter--close"

View file

@ -55,17 +55,17 @@ const validServer = withValidation<undefined, { error?: unknown }>({
{ {
key: "required", key: "required",
test: async ({ value }) => !!value, test: async ({ value }) => !!value,
invalid: () => _t("Enter a server name"), invalid: () => _t("spotlight|public_rooms|network_dropdown_required_invalid"),
}, },
{ {
key: "available", key: "available",
final: true, final: true,
test: async (_, { error }) => !error, test: async (_, { error }) => !error,
valid: () => _t("Looks good"), valid: () => _t("spotlight|public_rooms|network_dropdown_available_valid"),
invalid: ({ error }) => invalid: ({ error }) =>
error instanceof MatrixError && error.errcode === "M_FORBIDDEN" error instanceof MatrixError && error.errcode === "M_FORBIDDEN"
? _t("You are not allowed to view this server's rooms list") ? _t("spotlight|public_rooms|network_dropdown_available_invalid_forbidden")
: _t("Can't find this server or its room list"), : _t("spotlight|public_rooms|network_dropdown_available_invalid"),
}, },
], ],
memoize: true, memoize: true,
@ -151,7 +151,8 @@ export const NetworkDropdown: React.FC<IProps> = ({ protocols, config, setConfig
const options: GenericDropdownMenuItem<IPublicRoomDirectoryConfig | null>[] = allServers.map((roomServer) => ({ const options: GenericDropdownMenuItem<IPublicRoomDirectoryConfig | null>[] = allServers.map((roomServer) => ({
key: { roomServer, instanceId: undefined }, key: { roomServer, instanceId: undefined },
label: roomServer, label: roomServer,
description: roomServer === homeServer ? _t("Your server") : null, description:
roomServer === homeServer ? _t("spotlight|public_rooms|network_dropdown_your_server_description") : null,
options: [ options: [
{ {
key: { roomServer, instanceId: undefined }, key: { roomServer, instanceId: undefined },
@ -171,7 +172,7 @@ export const NetworkDropdown: React.FC<IProps> = ({ protocols, config, setConfig
adornment: ( adornment: (
<AccessibleButton <AccessibleButton
className="mx_NetworkDropdown_removeServer" className="mx_NetworkDropdown_removeServer"
alt={_t("Remove server “%(roomServer)s”", { roomServer })} alt={_t("spotlight|public_rooms|network_dropdown_remove_server_adornment", { roomServer })}
onClick={() => setUserDefinedServers(without(userDefinedServers, roomServer))} onClick={() => setUserDefinedServers(without(userDefinedServers, roomServer))}
/> />
), ),
@ -191,11 +192,11 @@ export const NetworkDropdown: React.FC<IProps> = ({ protocols, config, setConfig
const { finished } = Modal.createDialog( const { finished } = Modal.createDialog(
TextInputDialog, TextInputDialog,
{ {
title: _t("Add a new server"), title: _t("spotlight|public_rooms|network_dropdown_add_dialog_title"),
description: _t("Enter the name of a new server you want to explore."), description: _t("spotlight|public_rooms|network_dropdown_add_dialog_description"),
button: _t("action|add"), button: _t("action|add"),
hasCancel: false, hasCancel: false,
placeholder: _t("Server name"), placeholder: _t("spotlight|public_rooms|network_dropdown_add_dialog_placeholder"),
validator: validServer, validator: validServer,
fixedWidth: false, fixedWidth: false,
}, },
@ -214,7 +215,9 @@ export const NetworkDropdown: React.FC<IProps> = ({ protocols, config, setConfig
}} }}
> >
<div className="mx_GenericDropdownMenu_Option--label"> <div className="mx_GenericDropdownMenu_Option--label">
<span className="mx_NetworkDropdown_addServer">{_t("Add new server…")}</span> <span className="mx_NetworkDropdown_addServer">
{_t("spotlight|public_rooms|network_dropdown_add_server_option")}
</span>
</div> </div>
</MenuItemRadio> </MenuItemRadio>
</> </>
@ -233,11 +236,11 @@ export const NetworkDropdown: React.FC<IProps> = ({ protocols, config, setConfig
onChange={(option) => setConfig(option)} onChange={(option) => setConfig(option)}
selectedLabel={(option) => selectedLabel={(option) =>
option?.key option?.key
? _t("Show: %(instance)s rooms (%(server)s)", { ? _t("spotlight|public_rooms|network_dropdown_selected_label_instance", {
server: option.key.roomServer, server: option.key.roomServer,
instance: option.key.instanceId ? option.label : "Matrix", instance: option.key.instanceId ? option.label : "Matrix",
}) })
: _t("Show: Matrix rooms") : _t("spotlight|public_rooms|network_dropdown_selected_label")
} }
AdditionalOptions={addNewServer} AdditionalOptions={addNewServer}
/> />

View file

@ -42,7 +42,7 @@ export default class InfoTooltip extends React.PureComponent<ITooltipProps> {
public render(): React.ReactNode { public render(): React.ReactNode {
const { tooltip, children, tooltipClassName, className, kind } = this.props; const { tooltip, children, tooltipClassName, className, kind } = this.props;
const title = _t("Information"); const title = _t("info_tooltip_title");
const iconClassName = const iconClassName =
kind !== InfoTooltipKind.Warning ? "mx_InfoTooltip_icon_info" : "mx_InfoTooltip_icon_warning"; kind !== InfoTooltipKind.Warning ? "mx_InfoTooltip_icon_info" : "mx_InfoTooltip_icon_warning";

View file

@ -130,7 +130,7 @@ export default class LanguageDropdown extends React.Component<IProps, IState> {
onSearchChange={this.onSearchChange} onSearchChange={this.onSearchChange}
searchEnabled={true} searchEnabled={true}
value={value} value={value}
label={_t("Language Dropdown")} label={_t("language_dropdown_label")}
disabled={this.props.disabled} disabled={this.props.disabled}
> >
{options} {options}

View file

@ -125,7 +125,7 @@ export const Pill: React.FC<PillProps> = ({ type: propType, url, inMessage, room
case PillType.EventInOtherRoom: case PillType.EventInOtherRoom:
{ {
avatar = <PillRoomAvatar shouldShowPillAvatar={shouldShowPillAvatar} room={targetRoom} />; avatar = <PillRoomAvatar shouldShowPillAvatar={shouldShowPillAvatar} room={targetRoom} />;
pillText = _t("Message in %(room)s", { pillText = _t("pill|permalink_other_room", {
room: text, room: text,
}); });
} }
@ -134,7 +134,7 @@ export const Pill: React.FC<PillProps> = ({ type: propType, url, inMessage, room
{ {
if (event) { if (event) {
avatar = <PillMemberAvatar shouldShowPillAvatar={shouldShowPillAvatar} member={member} />; avatar = <PillMemberAvatar shouldShowPillAvatar={shouldShowPillAvatar} member={member} />;
pillText = _t("Message from %(user)s", { pillText = _t("pill|permalink_this_room", {
user: text, user: text,
}); });
} else { } else {

View file

@ -151,7 +151,7 @@ export default class PowerSelector<K extends undefined | string> extends React.C
public render(): React.ReactNode { public render(): React.ReactNode {
let picker; let picker;
const label = typeof this.props.label === "undefined" ? _t("Power level") : this.props.label; const label = typeof this.props.label === "undefined" ? _t("power_level|label") : this.props.label;
if (this.state.custom) { if (this.state.custom) {
picker = ( picker = (
<Field <Field
@ -173,7 +173,7 @@ export default class PowerSelector<K extends undefined | string> extends React.C
text: Roles.textualPowerLevel(level, this.props.usersDefault), text: Roles.textualPowerLevel(level, this.props.usersDefault),
}; };
}); });
options.push({ value: CUSTOM_VALUE, text: _t("Custom level") }); options.push({ value: CUSTOM_VALUE, text: _t("power_level|custom_level") });
const optionsElements = options.map((op) => { const optionsElements = options.map((op) => {
return ( return (
<option value={op.value} key={op.value} data-testid={`power-level-option-${op.value}`}> <option value={op.value} key={op.value} data-testid={`power-level-option-${op.value}`}>

View file

@ -204,9 +204,7 @@ export default class ReplyChain extends React.Component<IProps, IState> {
if (this.state.err) { if (this.state.err) {
header = ( header = (
<blockquote className="mx_ReplyChain mx_ReplyChain_error"> <blockquote className="mx_ReplyChain mx_ReplyChain_error">
{_t( {_t("timeline|reply|error_loading")}
"Unable to load event that was replied to, it either does not exist or you do not have permission to view it.",
)}
</blockquote> </blockquote>
); );
} else if (this.state.loadedEv && shouldDisplayReply(this.state.events[0])) { } else if (this.state.loadedEv && shouldDisplayReply(this.state.events[0])) {
@ -215,7 +213,7 @@ export default class ReplyChain extends React.Component<IProps, IState> {
header = ( header = (
<blockquote className={`mx_ReplyChain ${this.getReplyChainColorClass(ev)}`}> <blockquote className={`mx_ReplyChain ${this.getReplyChainColorClass(ev)}`}>
{_t( {_t(
"<a>In reply to</a> <pill>", "timeline|reply|in_reply_to",
{}, {},
{ {
a: (sub) => ( a: (sub) => (
@ -244,7 +242,7 @@ export default class ReplyChain extends React.Component<IProps, IState> {
header = ( header = (
<p className="mx_ReplyChain_Export"> <p className="mx_ReplyChain_Export">
{_t( {_t(
"In reply to <a>this message</a>", "timeline|reply|in_reply_to_for_export",
{}, {},
{ {
a: (sub) => ( a: (sub) => (

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