Migrate more strings to translation keys (#11637)

This commit is contained in:
Michael Telatynski 2023-09-21 09:11:26 +01:00 committed by GitHub
parent fc9caa3269
commit d77b871769
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
107 changed files with 7689 additions and 6497 deletions

View file

@ -119,7 +119,7 @@ const FileDropTarget: React.FC<IProps> = ({ parent, onFileDrop }) => {
className="mx_FileDropTarget_image"
alt=""
/>
{_t("Drop file here to upload")}
{_t("room|drop_file_prompt")}
</div>
);
}

View file

@ -231,7 +231,7 @@ class FilePanel extends React.Component<IProps, IState> {
<BaseCard className="mx_FilePanel mx_RoomView_messageListWrapper" onClose={this.props.onClose}>
<div className="mx_RoomView_empty">
{_t(
"You must <a>register</a> to use this functionality",
"file_panel|guest_note",
{},
{
a: (sub) => (
@ -247,7 +247,7 @@ class FilePanel extends React.Component<IProps, IState> {
} else if (this.noRoom) {
return (
<BaseCard className="mx_FilePanel mx_RoomView_messageListWrapper" onClose={this.props.onClose}>
<div className="mx_RoomView_empty">{_t("You must join the room to see its files")}</div>
<div className="mx_RoomView_empty">{_t("file_panel|peek_note")}</div>
</BaseCard>
);
}
@ -256,8 +256,8 @@ class FilePanel extends React.Component<IProps, IState> {
const emptyState = (
<div className="mx_RightPanel_empty mx_FilePanel_empty">
<h2>{_t("No files visible in this room")}</h2>
<p>{_t("Attach files from chat or just drag and drop them anywhere in a room.")}</p>
<h2>{_t("file_panel|empty_heading")}</h2>
<p>{_t("file_panel|empty_description")}</p>
</div>
);

View file

@ -515,12 +515,8 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
const normalFontSize = "15px";
const waitText = _t("Wait!");
const scamText = _t(
"If someone told you to copy/paste something here, there is a high likelihood you're being scammed!",
);
const devText = _t(
"If you know what you're doing, Element is open-source, be sure to check out our GitHub (https://github.com/vector-im/element-web/) and contribute!",
);
const scamText = _t("console_scam_warning");
const devText = _t("console_dev_note");
global.mx_rage_logger.bypassRageshake(
"log",

View file

@ -1232,9 +1232,9 @@ class CreationGrouper extends BaseGrouper {
const roomId = ev.getRoomId();
const creator = ev.sender?.name ?? ev.getSender();
if (roomId && DMRoomMap.shared().getUserIdForRoomId(roomId)) {
summaryText = _t("%(creator)s created this DM.", { creator });
summaryText = _t("timeline|creation_summary_dm", { creator });
} else {
summaryText = _t("%(creator)s created and configured the room.", { creator });
summaryText = _t("timeline|creation_summary_room", { creator });
}
ret.push(<NewRoomIntro key="newroomintro" />);

View file

@ -58,8 +58,8 @@ export default class NotificationPanel extends React.PureComponent<IProps, IStat
public render(): React.ReactNode {
const emptyState = (
<div className="mx_RightPanel_empty mx_NotificationPanel_empty">
<h2>{_t("You're all caught up")}</h2>
<p>{_t("You have no visible notifications.")}</p>
<h2>{_t("notif_panel|empty_heading")}</h2>
<p>{_t("notif_panel|empty_description")}</p>
</div>
);

View file

@ -245,9 +245,7 @@ const Tile: React.FC<ITileProps> = ({
let suggestedSection: ReactElement | undefined;
if (suggested && (!joinedRoom || hasPermissions)) {
suggestedSection = (
<InfoTooltip tooltip={_t("This room is suggested as a good one to join")}>{_t("Suggested")}</InfoTooltip>
);
suggestedSection = <InfoTooltip tooltip={_t("space|suggested_tooltip")}>{_t("space|suggested")}</InfoTooltip>;
}
const content = (
@ -670,14 +668,14 @@ const ManageButtons: React.FC<IManageButtonsProps> = ({ hierarchy, selected, set
if (!selectedRelations.length) {
Button = AccessibleTooltipButton;
props = {
tooltip: _t("Select a room below first"),
tooltip: _t("space|select_room_below"),
alignment: Alignment.Top,
};
}
let buttonText = _t("Saving…");
if (!saving) {
buttonText = selectionAllSuggested ? _t("Mark as not suggested") : _t("Mark as suggested");
buttonText = selectionAllSuggested ? _t("space|unmark_suggested") : _t("space|mark_suggested");
}
return (
@ -707,7 +705,7 @@ const ManageButtons: React.FC<IManageButtonsProps> = ({ hierarchy, selected, set
hierarchy.removeRelation(parentId, childId);
}
} catch (e) {
setError(_t("Failed to remove some rooms. Try again later"));
setError(_t("space|failed_remove_rooms"));
}
setRemoving(false);
setSelected(new Map());
@ -788,13 +786,13 @@ const SpaceHierarchy: React.FC<IProps> = ({ space, initialText = "", showRoom, a
const [error, setError] = useState("");
let errorText = error;
if (!error && hierarchyError) {
errorText = _t("Failed to load list of rooms.");
errorText = _t("space|failed_load_rooms");
}
const loaderRef = useIntersectionObserver(loadMore);
if (!loading && hierarchy!.noSupport) {
return <p>{_t("Your server does not support showing space hierarchies.")}</p>;
return <p>{_t("space|incompatible_server_hierarchy")}</p>;
}
const onKeyDown = (ev: KeyboardEvent, state: IState): void => {

View file

@ -354,7 +354,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
/>
<IconizedContextMenuOption
iconClassName="mx_UserMenu_iconLock"
label={_t("Security & Privacy")}
label={_t("room_settings|security|title")}
onClick={(e) => this.onSettingsOpen(e, UserTab.Security)}
/>
<IconizedContextMenuOption
@ -410,11 +410,16 @@ export default class UserMenu extends React.Component<IProps, IState> {
<RovingAccessibleTooltipButton
className="mx_UserMenu_contextMenu_themeButton"
onClick={this.onSwitchThemeClick}
title={this.state.isDarkTheme ? _t("Switch to light mode") : _t("Switch to dark mode")}
title={
this.state.isDarkTheme
? _t("user_menu|switch_theme_light")
: _t("user_menu|switch_theme_dark")
}
>
<img
src={require("../../../res/img/element-icons/roomlist/dark-light-mode.svg").default}
alt={_t("Switch theme")}
role="presentation"
alt=""
width={16}
/>
</RovingAccessibleTooltipButton>

View file

@ -79,14 +79,16 @@ export default class ViewSource extends React.Component<IProps, IState> {
<>
<details open className="mx_ViewSource_details">
<summary>
<span className="mx_ViewSource_heading">{_t("Decrypted event source")}</span>
<span className="mx_ViewSource_heading">
{_t("devtools|view_source_decrypted_event_source")}
</span>
</summary>
{decryptedEventSource ? (
<CopyableText getTextToCopy={copyDecryptedFunc}>
<SyntaxHighlight language="json">{stringify(decryptedEventSource)}</SyntaxHighlight>
</CopyableText>
) : (
<div>{_t("Decrypted source unavailable")}</div>
<div>{_t("devtools|view_source_decrypted_event_source_unavailable")}</div>
)}
</details>
<details className="mx_ViewSource_details">

View file

@ -169,7 +169,7 @@ export default class LoginWithQRFlow extends React.Component<IProps> {
);
break;
case Phase.ShowingQR:
title = _t("Sign in with QR code");
title = _t("settings|sessions|sign_in_with_qr");
if (this.props.code) {
const code = (
<div className="mx_LoginWithQR_qrWrapper">

View file

@ -358,7 +358,7 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
description: (_, results) => {
// omit the description if the only failing result is the `available` one as it makes no sense for it.
if (results.every(({ key, valid }) => key === "available" || valid)) return null;
return _t("Use lowercase letters, numbers, dashes and underscores only");
return _t("auth|registration_username_validation");
},
hideDescriptionIfValid: true,
async deriveData(this: RegistrationForm, { value }) {
@ -401,8 +401,8 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
},
invalid: (usernameAvailable) =>
usernameAvailable === UsernameAvailableStatus.Error
? _t("Unable to check if username has been taken. Try again later.")
: _t("Someone already has that username. Try another or if it is you, sign in below."),
? _t("auth|registration_username_unable_check")
: _t("auth|registration_username_in_use"),
},
],
});
@ -496,7 +496,9 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
if (!this.showPhoneNumber()) {
return null;
}
const phoneLabel = this.authStepIsRequired("m.login.msisdn") ? _t("Phone") : _t("Phone (optional)");
const phoneLabel = this.authStepIsRequired("m.login.msisdn")
? _t("auth|phone_label")
: _t("auth|phone_optional_label");
const phoneCountry = (
<CountryDropdown
value={this.state.phoneCountry}
@ -549,15 +551,13 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
if (this.showPhoneNumber()) {
emailHelperText = (
<div>
{_t("Add an email to be able to reset your password.")}{" "}
{_t("Use email or phone to optionally be discoverable by existing contacts.")}
{_t("auth|email_help_text")} {_t("auth|email_phone_discovery_text")}
</div>
);
} else {
emailHelperText = (
<div>
{_t("Add an email to be able to reset your password.")}{" "}
{_t("Use email to optionally be discoverable by existing contacts.")}
{_t("auth|email_help_text")} {_t("auth|email_discovery_text")}
</div>
);
}

View file

@ -132,7 +132,7 @@ const SpaceContextMenu: React.FC<IProps> = ({ space, hideHeader, onFinished, ...
devtoolsOption = (
<IconizedContextMenuOption
iconClassName="mx_SpacePanel_iconSettings"
label={_t("See room timeline (devtools)")}
label={_t("space|context_menu|devtools_open_timeline")}
onClick={onViewTimelineClick}
/>
);
@ -243,13 +243,13 @@ const SpaceContextMenu: React.FC<IProps> = ({ space, hideHeader, onFinished, ...
<IconizedContextMenuOptionList first>
<IconizedContextMenuOption
iconClassName="mx_SpacePanel_iconHome"
label={_t("Space home")}
label={_t("space|context_menu|home")}
onClick={onHomeClick}
/>
{inviteOption}
<IconizedContextMenuOption
iconClassName="mx_SpacePanel_iconExplore"
label={canAddRooms ? _t("Manage & explore rooms") : _t("Explore rooms")}
label={canAddRooms ? _t("space|context_menu|manage_and_explore") : _t("space|context_menu|explore")}
onClick={onExploreRoomsClick}
/>
<IconizedContextMenuOption

View file

@ -88,9 +88,9 @@ export function ManualDeviceKeyVerificationDialog({
return (
<QuestionDialog
title={_t("Verify session")}
title={_t("settings|sessions|verify_session")}
description={body}
button={_t("Verify session")}
button={_t("settings|sessions|verify_session")}
onFinished={onLegacyFinished}
/>
);

View file

@ -163,7 +163,7 @@ class RoomSettingsDialog extends React.Component<IProps, IState> {
tabs.push(
new Tab(
RoomSettingsTab.Security,
_td("Security & Privacy"),
_td("room_settings|security|title"),
"mx_RoomSettingsDialog_securityIcon",
<SecurityRoomSettingsTab room={this.state.room} closeSettingsFn={() => this.props.onFinished(true)} />,
"RoomSettingsSecurityPrivacy",
@ -172,7 +172,7 @@ class RoomSettingsDialog extends React.Component<IProps, IState> {
tabs.push(
new Tab(
RoomSettingsTab.Roles,
_td("Roles & Permissions"),
_td("room_settings|permissions|title"),
"mx_RoomSettingsDialog_rolesIcon",
<RolesRoomSettingsTab room={this.state.room} />,
"RoomSettingsRolesPermissions",

View file

@ -80,8 +80,8 @@ export const SlidingSyncOptionsDialog: React.FC<{ onFinished(enabled: boolean):
nativeSupport = _t("Checking…");
} else {
nativeSupport = hasNativeSupport
? _t("Your server has native support")
: _t("Your server lacks native support");
? _t("labs|sliding_sync_server_support")
: _t("labs|sliding_sync_server_no_support");
}
const validProxy = withValidation<undefined, { error?: unknown }>({
@ -97,7 +97,7 @@ export const SlidingSyncOptionsDialog: React.FC<{ onFinished(enabled: boolean):
{
key: "required",
test: async ({ value }) => !!value || !!hasNativeSupport,
invalid: () => _t("Your server lacks native support, you must specify a proxy"),
invalid: () => _t("labs|sliding_sync_server_specify_proxy"),
},
{
key: "working",
@ -111,16 +111,20 @@ export const SlidingSyncOptionsDialog: React.FC<{ onFinished(enabled: boolean):
return (
<TextInputDialog
title={_t("Sliding Sync configuration")}
title={_t("labs|sliding_sync_configuration")}
description={
<div>
<div>
<b>{_t("To disable you will need to log out and back in, use with caution!")}</b>
<b>{_t("labs|sliding_sync_disable_warning")}</b>
</div>
{nativeSupport}
</div>
}
placeholder={hasNativeSupport ? _t("Proxy URL (optional)") : _t("Proxy URL")}
placeholder={
hasNativeSupport
? _t("labs|sliding_sync_proxy_url_optional_label")
: _t("labs|sliding_sync_proxy_url_label")
}
value={currentProxy}
button={_t("action|enable")}
validator={validProxy}

View file

@ -67,7 +67,7 @@ const SpaceSettingsDialog: React.FC<IProps> = ({ matrixClient: cli, space, onFin
),
new Tab(
SpaceSettingsTab.Roles,
_td("Roles & Permissions"),
_td("room_settings|permissions|title"),
"mx_RoomSettingsDialog_rolesIcon",
<RolesRoomSettingsTab room={space} />,
),
@ -84,7 +84,7 @@ const SpaceSettingsDialog: React.FC<IProps> = ({ matrixClient: cli, space, onFin
return (
<BaseDialog
title={_t("Settings - %(spaceName)s", { spaceName: space.name || _t("common|unnamed_space") })}
title={_t("space_settings|title", { spaceName: space.name || _t("common|unnamed_space") })}
className="mx_SpaceSettingsDialog"
contentId="mx_SpaceSettingsDialog"
onFinished={onFinished}

View file

@ -117,7 +117,7 @@ export default class TermsDialog extends React.PureComponent<ITermsDialogProps,
</div>
);
case SERVICE_TYPES.IM:
return <div>{_t("Use bots, bridges, widgets and sticker packs")}</div>;
return <div>{_t("terms|integration_manager")}</div>;
}
}
@ -192,19 +192,19 @@ export default class TermsDialog extends React.PureComponent<ITermsDialogProps,
<BaseDialog
fixedWidth={false}
onFinished={this.onCancelClick}
title={_t("Terms of Service")}
title={_t("terms|tos")}
contentId="mx_Dialog_content"
hasCancel={false}
>
<div id="mx_Dialog_content">
<p>{_t("To continue you need to accept the terms of this service.")}</p>
<p>{_t("terms|intro")}</p>
<table className="mx_TermsDialog_termsTable">
<tbody>
<tr className="mx_TermsDialog_termsTableHeader">
<th>{_t("Service")}</th>
<th>{_t("Summary")}</th>
<th>{_t("Document")}</th>
<th>{_t("terms|column_service")}</th>
<th>{_t("terms|column_summary")}</th>
<th>{_t("terms|column_document")}</th>
<th>{_t("action|accept")}</th>
</tr>
{rows}

View file

@ -144,7 +144,7 @@ export default class UserSettingsDialog extends React.Component<IProps, IState>
tabs.push(
new Tab(
UserTab.Security,
_td("Security & Privacy"),
_td("room_settings|security|title"),
"mx_UserSettingsDialog_securityIcon",
<SecurityUserSettingsTab closeSettingsFn={this.props.onFinished} />,
"UserSettingsSecurityPrivacy",

View file

@ -78,7 +78,7 @@ export default class RoomPublishSetting extends React.PureComponent<IProps, ISta
value={this.state.isRoomPublished}
onChange={this.onRoomPublishChange}
disabled={!enabled}
label={_t("Publish this room to the public in %(domain)s's room directory?", {
label={_t("room_settings|general|publish_toggle", {
domain: client.getDomain(),
})}
/>

View file

@ -53,7 +53,7 @@ export default class UrlPreviewSettings extends React.Component<IProps> {
const accountEnabled = SettingsStore.getValueAt(SettingLevel.ACCOUNT, "urlPreviewsEnabled");
if (accountEnabled) {
previewsForAccount = _t(
"You have <a>enabled</a> URL previews by default.",
"room_settings|general|user_url_previews_default_on",
{},
{
a: (sub) => (
@ -65,7 +65,7 @@ export default class UrlPreviewSettings extends React.Component<IProps> {
);
} else {
previewsForAccount = _t(
"You have <a>disabled</a> URL previews by default.",
"room_settings|general|user_url_previews_default_off",
{},
{
a: (sub) => (
@ -87,16 +87,14 @@ export default class UrlPreviewSettings extends React.Component<IProps> {
/>
);
} else {
let str = _td("URL previews are enabled by default for participants in this room.");
let str = _td("room_settings|general|default_url_previews_on");
if (!SettingsStore.getValueAt(SettingLevel.ROOM, "urlPreviewsEnabled", roomId, /*explicit=*/ true)) {
str = _td("URL previews are disabled by default for participants in this room.");
str = _td("room_settings|general|default_url_previews_off");
}
previewsForRoom = <div>{_t(str)}</div>;
}
} else {
previewsForAccount = _t(
"In encrypted rooms, like this one, URL previews are disabled by default to ensure that your homeserver (where the previews are generated) cannot gather information about links you see in this room.",
);
previewsForAccount = _t("room_settings|general|url_preview_encryption_warning");
}
const previewsForRoomAccount = // in an e2ee room we use a special key to enforce per-room opt-in
@ -110,17 +108,13 @@ export default class UrlPreviewSettings extends React.Component<IProps> {
const description = (
<>
<p>
{_t(
"When someone puts a URL in their message, a URL preview can be shown to give more information about that link such as the title, description, and an image from the website.",
)}
</p>
<p>{_t("room_settings|general|url_preview_explainer")}</p>
<p>{previewsForAccount}</p>
</>
);
return (
<SettingsFieldset legend={_t("URL Previews")} description={description}>
<SettingsFieldset legend={_t("room_settings|general|url_previews_section")} description={description}>
{previewsForRoom}
{previewsForRoomAccount}
</SettingsFieldset>

View file

@ -813,7 +813,7 @@ export default class RoomHeader extends React.Component<IProps, IState> {
initialTabId: UserTab.Labs,
});
const betaPill = isVideoRoom ? (
<BetaPill onClick={viewLabs} tooltipTitle={_t("Video rooms are a beta feature")} />
<BetaPill onClick={viewLabs} tooltipTitle={_t("labs|video_rooms_beta")} />
) : null;
return (

View file

@ -46,14 +46,14 @@ function hasExpectedEncryptionSettings(matrixClient: MatrixClient, room: Room):
const determineIntroMessage = (room: Room, encryptedSingle3rdPartyInvite: boolean): TranslationKey => {
if (room instanceof LocalRoom) {
return _td("Send your first message to invite <displayName/> to chat");
return _td("room|intro|send_message_start_dm");
}
if (encryptedSingle3rdPartyInvite) {
return _td("Once everyone has joined, youll be able to chat");
return _td("room|intro|encrypted_3pid_dm_pending_join");
}
return _td("This is the beginning of your direct message history with <displayName/>.");
return _td("room|intro|start_of_dm_history");
};
const NewRoomIntro: React.FC = () => {
@ -78,7 +78,7 @@ const NewRoomIntro: React.FC = () => {
!encryptedSingle3rdPartyInvite &&
room.getJoinedMemberCount() + room.getInvitedMemberCount() === 2
) {
caption = _t("Only the two of you are in this conversation, unless either of you invites anyone to join.");
caption = _t("room|intro|dm_caption");
}
const member = room?.getMember(dmPartner);
@ -133,7 +133,7 @@ const NewRoomIntro: React.FC = () => {
let topicText;
if (canAddTopic && topic) {
topicText = _t(
"Topic: %(topic)s (<a>edit</a>)",
"room|intro|topic_edit",
{ topic },
{
a: (sub) => (
@ -144,10 +144,10 @@ const NewRoomIntro: React.FC = () => {
},
);
} else if (topic) {
topicText = _t("Topic: %(topic)s ", { topic });
topicText = _t("room|intro|topic", { topic });
} else if (canAddTopic) {
topicText = _t(
"<a>Add a topic</a> to help people know what it is about.",
"room|intro|no_topic",
{},
{
a: (sub) => (
@ -164,9 +164,9 @@ const NewRoomIntro: React.FC = () => {
let createdText: string;
if (creator === cli.getUserId()) {
createdText = _t("You created this room.");
createdText = _t("room|intro|you_created");
} else {
createdText = _t("%(displayName)s created this room.", {
createdText = _t("room|intro|user_created", {
displayName: creatorName,
});
}
@ -200,7 +200,7 @@ const NewRoomIntro: React.FC = () => {
defaultDispatcher.dispatch({ action: "view_invite", roomId });
}}
>
{_t("Invite to just this room")}
{_t("room|intro|room_invite")}
</AccessibleButton>
)}
</div>
@ -228,7 +228,7 @@ const NewRoomIntro: React.FC = () => {
avatar = (
<MiniAvatarUploader
hasAvatar={false}
noAvatarLabel={_t("Add a photo, so people can easily spot your room.")}
noAvatarLabel={_t("room|intro|no_avatar_label")}
setAvatarUrl={(url) => cli.sendStateEvent(roomId, EventType.RoomAvatar, { url }, "")}
>
{avatar}
@ -245,7 +245,7 @@ const NewRoomIntro: React.FC = () => {
<p>
{createdText}{" "}
{_t(
"This is the start of <roomName/>.",
"room|intro|start_of_room",
{},
{
roomName: () => <b>{room.name}</b>,
@ -266,9 +266,7 @@ const NewRoomIntro: React.FC = () => {
});
}
const subText = _t(
"Your private messages are normally encrypted, but this room isn't. Usually this is due to an unsupported device or method being used, like email invites.",
);
const subText = _t("room|intro|private_unencrypted_warning");
let subButton: JSX.Element | undefined;
if (
@ -277,7 +275,7 @@ const NewRoomIntro: React.FC = () => {
) {
subButton = (
<AccessibleButton kind="link_inline" onClick={openRoomSettings}>
{_t("Enable encryption in settings.")}
{_t("room|intro|enable_encryption_prompt")}
</AccessibleButton>
);
}
@ -294,7 +292,7 @@ const NewRoomIntro: React.FC = () => {
{!hasExpectedEncryptionSettings(cli, room) && (
<EventTileBubble
className="mx_cryptoEvent mx_cryptoEvent_icon_warning"
title={_t("End-to-end encryption isn't enabled")}
title={_t("room|intro|unencrypted_warning")}
subtitle={subtitle}
/>
)}

View file

@ -162,7 +162,7 @@ const RoomPreviewCard: FC<IProps> = ({ room, onJoinButtonClicked, onRejectButton
<>
<RoomAvatar room={room} size="50px" viewAvatarOnClick />
<div className="mx_RoomPreviewCard_video" />
<BetaPill onClick={viewLabs} tooltipTitle={_t("Video rooms are a beta feature")} />
<BetaPill onClick={viewLabs} tooltipTitle={_t("labs|video_rooms_beta")} />
</>
);
} else if (room.isSpaceRoom()) {

View file

@ -68,7 +68,7 @@ const CurrentDeviceSectionHeading: React.FC<CurrentDeviceSectionHeadingProps> =
? [
<IconizedContextMenuOption
key="sign-out-all-others"
label={_t("Sign out of all other sessions (%(otherSessionsCount)s)", { otherSessionsCount })}
label={_t("settings|sessions|sign_out_all_other_sessions", { otherSessionsCount })}
onClick={signOutAllOtherSessions}
isDestructive
/>,
@ -76,7 +76,7 @@ const CurrentDeviceSectionHeading: React.FC<CurrentDeviceSectionHeadingProps> =
: []),
];
return (
<SettingsSubsectionHeading heading={_t("Current session")}>
<SettingsSubsectionHeading heading={_t("settings|sessions|current_session")}>
<KebabContextMenu
disabled={disabled}
title={_t("common|options")}

View file

@ -60,7 +60,7 @@ const DeviceNameEditor: React.FC<Props & { stopEditing: () => void }> = ({ devic
return (
<form aria-disabled={isLoading} className="mx_DeviceDetailHeading_renameForm" onSubmit={onSubmit} method="post">
<p id={headingId} className="mx_DeviceDetailHeading_renameFormHeading">
{_t("Rename session")}
{_t("settings|sessions|rename_form_heading")}
</p>
<div>
<Field
@ -77,21 +77,13 @@ const DeviceNameEditor: React.FC<Props & { stopEditing: () => void }> = ({ devic
maxLength={100}
/>
<Caption id={descriptionId}>
{_t("Please be aware that session names are also visible to people you communicate with.")}
{_t("settings|sessions|rename_form_caption")}
<LearnMore
title={_t("Renaming sessions")}
title={_t("settings|sessions|rename_form_learn_more")}
description={
<>
<p>
{_t(
"Other users in direct messages and rooms that you join are able to view a full list of your sessions.",
)}
</p>
<p>
{_t(
"This provides them with confidence that they are really speaking to you, but it also means they can see the session name you enter here.",
)}
</p>
<p>{_t("settings|sessions|rename_form_learn_more_description_1")}</p>
<p>{_t("settings|sessions|rename_form_learn_more_description_2")}</p>
</>
}
/>

View file

@ -64,9 +64,9 @@ const DeviceDetails: React.FC<Props> = ({
{
id: "session",
values: [
{ label: _t("Session ID"), value: device.device_id },
{ label: _t("settings|sessions|session_id"), value: device.device_id },
{
label: _t("Last activity"),
label: _t("settings|sessions|last_activity"),
value: device.last_seen_ts && formatDate(new Date(device.last_seen_ts)),
},
],
@ -77,7 +77,7 @@ const DeviceDetails: React.FC<Props> = ({
values: [
{ label: _t("common|name"), value: device.appName },
{ label: _t("common|version"), value: device.appVersion },
{ label: _t("URL"), value: device.url },
{ label: _t("settings|sessions|url"), value: device.url },
],
},
{
@ -85,9 +85,9 @@ const DeviceDetails: React.FC<Props> = ({
heading: _t("common|device"),
values: [
{ label: _t("common|model"), value: device.deviceModel },
{ label: _t("Operating system"), value: device.deviceOperatingSystem },
{ label: _t("Browser"), value: device.client },
{ label: _t("IP address"), value: device.last_seen_ip },
{ label: _t("settings|sessions|os"), value: device.deviceOperatingSystem },
{ label: _t("settings|sessions|browser"), value: device.client },
{ label: _t("settings|sessions|ip"), value: device.last_seen_ip },
],
},
]
@ -122,7 +122,7 @@ const DeviceDetails: React.FC<Props> = ({
<DeviceVerificationStatusCard device={device} onVerifyDevice={onVerifyDevice} isCurrentDevice />
</section>
<section className="mx_DeviceDetails_section">
<p className="mx_DeviceDetails_sectionHeading">{_t("Session details")}</p>
<p className="mx_DeviceDetails_sectionHeading">{_t("settings|sessions|details_heading")}</p>
{metadata.map(({ heading, values, id }, index) => (
<table
className="mx_DeviceDetails_metadataTable"
@ -158,13 +158,13 @@ const DeviceDetails: React.FC<Props> = ({
checked={isPushNotificationsEnabled(pusher, localNotificationSettings)}
disabled={isCheckboxDisabled(pusher, localNotificationSettings)}
onChange={(checked) => setPushNotifications?.(device.device_id, checked)}
title={_t("Toggle push notifications on this session.")}
title={_t("settings|sessions|push_toggle")}
data-testid="device-detail-push-notification-checkbox"
/>
<p className="mx_DeviceDetails_sectionHeading">
{_t("Push notifications")}
{_t("settings|sessions|push_heading")}
<small className="mx_DeviceDetails_sectionSubheading">
{_t("Receive push notifications on this session.")}
{_t("settings|sessions|push_subheading")}
</small>
</p>
</section>
@ -177,7 +177,7 @@ const DeviceDetails: React.FC<Props> = ({
data-testid="device-detail-sign-out-cta"
>
<span className="mx_DeviceDetails_signOutButtonContent">
{_t("Sign out of this session")}
{_t("settings|sessions|sign_out")}
{isSigningOut && <Spinner w={16} h={16} />}
</span>
</AccessibleButton>

View file

@ -27,7 +27,7 @@ interface Props extends React.ComponentProps<typeof AccessibleTooltipButton> {
}
export const DeviceExpandDetailsButton: React.FC<Props> = ({ isExpanded, onClick, ...rest }) => {
const label = isExpanded ? _t("Hide details") : _t("Show details");
const label = isExpanded ? _t("settings|sessions|hide_details") : _t("settings|sessions|show_details");
return (
<AccessibleTooltipButton
{...rest}

View file

@ -50,7 +50,7 @@ const getInactiveMetadata = (device: ExtendedDevice): { id: string; value: React
value: (
<>
<InactiveIcon className="mx_DeviceTile_inactiveIcon" />
{_t("Inactive for %(inactiveAgeDays)s+ days", { inactiveAgeDays: INACTIVE_DEVICE_AGE_DAYS }) +
{_t("settings|sessions|inactive_days", { inactiveAgeDays: INACTIVE_DEVICE_AGE_DAYS }) +
` (${formatLastActivity(device.last_seen_ts)})`}
</>
),
@ -62,7 +62,8 @@ const DeviceMetaDatum: React.FC<{ value: string | React.ReactNode; id: string }>
export const DeviceMetaData: React.FC<Props> = ({ device }) => {
const inactive = getInactiveMetadata(device);
const lastActivity = device.last_seen_ts && `${_t("Last activity")} ${formatLastActivity(device.last_seen_ts)}`;
const lastActivity =
device.last_seen_ts && `${_t("settings|sessions|last_activity")} ${formatLastActivity(device.last_seen_ts)}`;
const verificationStatus = device.isVerified ? _t("common|verified") : _t("common|unverified");
// if device is inactive, don't display last activity or verificationStatus
const metadata = inactive

View file

@ -32,73 +32,41 @@ const securityCardContent: Record<
}
> = {
[DeviceSecurityVariation.Verified]: {
title: _t("Verified sessions"),
title: _t("settings|sessions|verified_sessions"),
description: (
<>
<p>
{_t(
"Verified sessions are anywhere you are using this account after entering your passphrase or confirming your identity with another verified session.",
)}
</p>
<p>
{_t(
"This means that you have all the keys needed to unlock your encrypted messages and confirm to other users that you trust this session.",
)}
</p>
<p>{_t("settings|sessions|verified_sessions_explainer_1")}</p>
<p>{_t("settings|sessions|verified_sessions_explainer_2")}</p>
</>
),
},
[DeviceSecurityVariation.Unverified]: {
title: _t("Unverified sessions"),
title: _t("settings|sessions|unverified_sessions"),
description: (
<>
<p>
{_t(
"Unverified sessions are sessions that have logged in with your credentials but have not been cross-verified.",
)}
</p>
<p>
{_t(
"You should make especially certain that you recognise these sessions as they could represent an unauthorised use of your account.",
)}
</p>
<p>{_t("settings|sessions|unverified_sessions_explainer_1")}</p>
<p>{_t("settings|sessions|unverified_sessions_explainer_2")}</p>
</>
),
},
// unverifiable uses single-session case
// because it is only ever displayed on a single session detail
[DeviceSecurityVariation.Unverifiable]: {
title: _t("Unverified session"),
title: _t("settings|sessions|unverified_session"),
description: (
<>
<p>{_t(`This session doesn't support encryption and thus can't be verified.`)}</p>
<p>
{_t(
`You won't be able to participate in rooms where encryption is enabled when using this session.`,
)}
</p>
<p>
{_t(
`For best security and privacy, it is recommended to use Matrix clients that support encryption.`,
)}
</p>
<p>{_t("settings|sessions|unverified_session_explainer_1")}</p>
<p>{_t("settings|sessions|unverified_session_explainer_2")}</p>
<p>{_t("settings|sessions|unverified_session_explainer_3")}</p>
</>
),
},
[DeviceSecurityVariation.Inactive]: {
title: _t("Inactive sessions"),
title: _t("settings|sessions|inactive_sessions"),
description: (
<>
<p>
{_t(
"Inactive sessions are sessions you have not used in some time, but they continue to receive encryption keys.",
)}
</p>
<p>
{_t(
"Removing inactive sessions improves security and performance, and makes it easier for you to identify if a new session is suspicious.",
)}
</p>
<p>{_t("settings|sessions|inactive_sessions_explainer_1")}</p>
<p>{_t("settings|sessions|inactive_sessions_explainer_2")}</p>
</>
),
},

View file

@ -23,7 +23,7 @@ import { Icon as WebIcon } from "../../../../../res/img/element-icons/settings/w
import { Icon as MobileIcon } from "../../../../../res/img/element-icons/settings/mobile.svg";
import { Icon as VerifiedIcon } from "../../../../../res/img/e2e/verified.svg";
import { Icon as UnverifiedIcon } from "../../../../../res/img/e2e/warning.svg";
import { _t } from "../../../../languageHandler";
import { _t, _td, TranslationKey } from "../../../../languageHandler";
import { ExtendedDevice } from "./types";
import { DeviceType } from "../../../../utils/device/parseUserAgent";
@ -39,16 +39,16 @@ const deviceTypeIcon: Record<DeviceType, React.FC<React.SVGProps<SVGSVGElement>>
[DeviceType.Web]: WebIcon,
[DeviceType.Unknown]: UnknownDeviceIcon,
};
const deviceTypeLabel: Record<DeviceType, string> = {
[DeviceType.Desktop]: _t("Desktop session"),
[DeviceType.Mobile]: _t("Mobile session"),
[DeviceType.Web]: _t("Web session"),
[DeviceType.Unknown]: _t("Unknown session type"),
const deviceTypeLabel: Record<DeviceType, TranslationKey> = {
[DeviceType.Desktop]: _td("settings|sessions|desktop_session"),
[DeviceType.Mobile]: _td("settings|sessions|mobile_session"),
[DeviceType.Web]: _td("settings|sessions|web_session"),
[DeviceType.Unknown]: _td("settings|sessions|unknown_session"),
};
export const DeviceTypeIcon: React.FC<Props> = ({ isVerified, isSelected, deviceType }) => {
const Icon = deviceTypeIcon[deviceType!] || deviceTypeIcon[DeviceType.Unknown];
const label = deviceTypeLabel[deviceType!] || deviceTypeLabel[DeviceType.Unknown];
const label = _t(deviceTypeLabel[deviceType!] || deviceTypeLabel[DeviceType.Unknown]);
return (
<div
className={classNames("mx_DeviceTypeIcon", {

View file

@ -38,11 +38,11 @@ const getCardProps = (
} => {
if (device.isVerified) {
const descriptionText = isCurrentDevice
? _t("Your current session is ready for secure messaging.")
: _t("This session is ready for secure messaging.");
? _t("settings|sessions|device_verified_description_current")
: _t("settings|sessions|device_verified_description");
return {
variation: DeviceSecurityVariation.Verified,
heading: _t("Verified session"),
heading: _t("settings|sessions|verified_session"),
description: (
<>
{descriptionText}
@ -54,10 +54,10 @@ const getCardProps = (
if (device.isVerified === null) {
return {
variation: DeviceSecurityVariation.Unverified,
heading: _t("Unverified session"),
heading: _t("settings|sessions|unverified_session"),
description: (
<>
{_t(`This session doesn't support encryption and thus can't be verified.`)}
{_t("settings|sessions|unverified_session_explainer_1")}
<DeviceSecurityLearnMore variation={DeviceSecurityVariation.Unverifiable} />
</>
),
@ -65,11 +65,11 @@ const getCardProps = (
}
const descriptionText = isCurrentDevice
? _t("Verify your current session for enhanced secure messaging.")
: _t("Verify or sign out from this session for best security and reliability.");
? _t("settings|sessions|device_unverified_description_current")
: _t("settings|sessions|device_unverified_description");
return {
variation: DeviceSecurityVariation.Unverified,
heading: _t("Unverified session"),
heading: _t("settings|sessions|unverified_session"),
description: (
<>
{descriptionText}
@ -95,7 +95,7 @@ export const DeviceVerificationStatusCard: React.FC<DeviceVerificationStatusCard
onClick={onVerifyDevice}
data-testid={`verification-status-button-${device.device_id}`}
>
{_t("Verify session")}
{_t("settings|sessions|verify_session")}
</AccessibleButton>
)}
</DeviceSecurityCard>

View file

@ -73,36 +73,6 @@ const getFilteredSortedDevices = (devices: DevicesDictionary, filter?: FilterVar
const ALL_FILTER_ID = "ALL";
type DeviceFilterKey = FilterVariation | typeof ALL_FILTER_ID;
const securityCardContent: Record<
DeviceSecurityVariation,
{
title: string;
description: string;
}
> = {
[DeviceSecurityVariation.Verified]: {
title: _t("Verified sessions"),
description: _t("For best security, sign out from any session that you don't recognize or use anymore."),
},
[DeviceSecurityVariation.Unverified]: {
title: _t("Unverified sessions"),
description: _t(
"Verify your sessions for enhanced secure messaging or sign out from those you don't recognize or use anymore.",
),
},
[DeviceSecurityVariation.Unverifiable]: {
title: _t("Unverified session"),
description: _t(`This session doesn't support encryption and thus can't be verified.`),
},
[DeviceSecurityVariation.Inactive]: {
title: _t("Inactive sessions"),
description: _t(
"Consider signing out from old sessions (%(inactiveAgeDays)s days or older) you don't use anymore.",
{ inactiveAgeDays: INACTIVE_DEVICE_AGE_DAYS },
),
},
};
const isSecurityVariation = (filter?: DeviceFilterKey): filter is FilterVariation =>
!!filter &&
(
@ -115,6 +85,33 @@ const isSecurityVariation = (filter?: DeviceFilterKey): filter is FilterVariatio
const FilterSecurityCard: React.FC<{ filter?: DeviceFilterKey }> = ({ filter }) => {
if (isSecurityVariation(filter)) {
const securityCardContent: Record<
DeviceSecurityVariation,
{
title: string;
description: string;
}
> = {
[DeviceSecurityVariation.Verified]: {
title: _t("settings|sessions|verified_sessions"),
description: _t("settings|sessions|verified_sessions_list_description"),
},
[DeviceSecurityVariation.Unverified]: {
title: _t("settings|sessions|unverified_sessions"),
description: _t("settings|sessions|unverified_sessions_list_description"),
},
[DeviceSecurityVariation.Unverifiable]: {
title: _t("settings|sessions|unverified_session"),
description: _t("settings|sessions|unverified_session_explainer_1"),
},
[DeviceSecurityVariation.Inactive]: {
title: _t("settings|sessions|inactive_sessions"),
description: _t("settings|sessions|inactive_sessions_list_description", {
inactiveAgeDays: INACTIVE_DEVICE_AGE_DAYS,
}),
},
};
const { title, description } = securityCardContent[filter];
return (
<div className="mx_FilteredDeviceList_securityCard">
@ -138,13 +135,13 @@ const FilterSecurityCard: React.FC<{ filter?: DeviceFilterKey }> = ({ filter })
const getNoResultsMessage = (filter?: FilterVariation): string => {
switch (filter) {
case DeviceSecurityVariation.Verified:
return _t("No verified sessions found.");
return _t("settings|sessions|no_verified_sessions");
case DeviceSecurityVariation.Unverified:
return _t("No unverified sessions found.");
return _t("settings|sessions|no_unverified_sessions");
case DeviceSecurityVariation.Inactive:
return _t("No inactive sessions found.");
return _t("settings|sessions|no_inactive_sessions");
default:
return _t("No sessions found.");
return _t("settings|sessions|no_sessions");
}
};
interface NoResultsProps {
@ -281,21 +278,21 @@ export const FilteredDeviceList = forwardRef(
};
const options: FilterDropdownOption<DeviceFilterKey>[] = [
{ id: ALL_FILTER_ID, label: _t("All") },
{ id: ALL_FILTER_ID, label: _t("settings|sessions|filter_all") },
{
id: DeviceSecurityVariation.Verified,
label: _t("common|verified"),
description: _t("Ready for secure messaging"),
description: _t("settings|sessions|filter_verified_description"),
},
{
id: DeviceSecurityVariation.Unverified,
label: _t("common|unverified"),
description: _t("Not ready for secure messaging"),
description: _t("settings|sessions|filter_unverified_description"),
},
{
id: DeviceSecurityVariation.Inactive,
label: _t("Inactive"),
description: _t("Inactive for %(inactiveAgeDays)s days or longer", {
label: _t("settings|sessions|filter_inactive"),
description: _t("settings|sessions|filter_inactive_description", {
inactiveAgeDays: INACTIVE_DEVICE_AGE_DAYS,
}),
},
@ -349,7 +346,7 @@ export const FilteredDeviceList = forwardRef(
) : (
<FilterDropdown<DeviceFilterKey>
id="device-list-filter"
label={_t("Filter devices")}
label={_t("settings|sessions|filter_label")}
value={filter || ALL_FILTER_ID}
onOptionChange={onFilterOptionChange}
options={options}

View file

@ -37,7 +37,7 @@ const FilteredDeviceListHeader: React.FC<Props> = ({
children,
...rest
}) => {
const checkboxLabel = isAllSelected ? _t("Deselect all") : _t("Select all");
const checkboxLabel = isAllSelected ? _t("common|deselect_all") : _t("common|select_all");
return (
<div className="mx_FilteredDeviceListHeader" {...rest}>
{!isSelectDisabled && (
@ -54,7 +54,7 @@ const FilteredDeviceListHeader: React.FC<Props> = ({
)}
<span className="mx_FilteredDeviceListHeader_label">
{selectedDeviceCount > 0
? _t("%(count)s sessions selected", { count: selectedDeviceCount })
? _t("settings|sessions|n_sessions_selected", { count: selectedDeviceCount })
: _t("Sessions")}
</span>
{children}

View file

@ -52,15 +52,13 @@ export default class LoginWithQRSection extends React.Component<IProps> {
}
return (
<SettingsSubsection heading={_t("Sign in with QR code")}>
<SettingsSubsection heading={_t("settings|sessions|sign_in_with_qr")}>
<div className="mx_LoginWithQRSection">
<p className="mx_SettingsTab_subsectionText">
{_t(
"You can use this device to sign in a new device with a QR code. You will need to scan the QR code shown on this device with your device that's signed out.",
)}
{_t("settings|sessions|sign_in_with_qr_description")}
</p>
<AccessibleButton onClick={this.props.onShowQr} kind="primary">
{_t("Show QR code")}
{_t("settings|sessions|sign_in_with_qr_button")}
</AccessibleButton>
</div>
</SettingsSubsection>

View file

@ -41,14 +41,14 @@ export const OtherSessionsSectionHeading: React.FC<Props> = ({
signOutAllOtherSessions ? (
<IconizedContextMenuOption
key="sign-out-all-others"
label={_t("Sign out of %(count)s sessions", { count: otherSessionsCount })}
label={_t("settings|sessions|sign_out_n_sessions", { count: otherSessionsCount })}
onClick={signOutAllOtherSessions}
isDestructive
/>
) : null,
]);
return (
<SettingsSubsectionHeading heading={_t("Other sessions")}>
<SettingsSubsectionHeading heading={_t("settings|sessions|other_sessions_heading")}>
{!!menuOptions.length && (
<KebabContextMenu
disabled={disabled}

View file

@ -52,19 +52,17 @@ const SecurityRecommendations: React.FC<Props> = ({ devices, currentDeviceId, go
return (
<SettingsSubsection
heading={_t("Security recommendations")}
description={_t("Improve your account security by following these recommendations.")}
heading={_t("settings|sessions|security_recommendations")}
description={_t("settings|sessions|security_recommendations_description")}
data-testid="security-recommendations-section"
>
{!!unverifiedDevicesCount && (
<DeviceSecurityCard
variation={DeviceSecurityVariation.Unverified}
heading={_t("Unverified sessions")}
heading={_t("settings|sessions|unverified_sessions")}
description={
<>
{_t(
"Verify your sessions for enhanced secure messaging or sign out from those you don't recognize or use anymore.",
)}
{_t("settings|sessions|unverified_sessions_list_description")}
<DeviceSecurityLearnMore variation={DeviceSecurityVariation.Unverified} />
</>
}
@ -83,13 +81,10 @@ const SecurityRecommendations: React.FC<Props> = ({ devices, currentDeviceId, go
{!!unverifiedDevicesCount && <div className="mx_SecurityRecommendations_spacing" />}
<DeviceSecurityCard
variation={DeviceSecurityVariation.Inactive}
heading={_t("Inactive sessions")}
heading={_t("settings|sessions|inactive_sessions")}
description={
<>
{_t(
"Consider signing out from old sessions (%(inactiveAgeDays)s days or older) you don't use anymore.",
{ inactiveAgeDays },
)}
{_t("settings|sessions|inactive_sessions_list_description", { inactiveAgeDays })}
<DeviceSecurityLearnMore variation={DeviceSecurityVariation.Inactive} />
</>
}

View file

@ -53,20 +53,20 @@ export const deleteDevicesWithInteractiveAuth = async (
const dialogAesthetics = {
[SSOAuthEntry.PHASE_PREAUTH]: {
title: _t("Use Single Sign On to continue"),
body: _t("Confirm logging out these devices by using Single Sign On to prove your identity.", {
body: _t("settings|sessions|confirm_sign_out_sso", {
count: numDevices,
}),
continueText: _t("auth|sso"),
continueKind: "primary",
},
[SSOAuthEntry.PHASE_POSTAUTH]: {
title: _t("Confirm signing out these devices", {
title: _t("settings|sessions|confirm_sign_out", {
count: numDevices,
}),
body: _t("Click the button below to confirm signing out these devices.", {
body: _t("settings|sessions|confirm_sign_out_body", {
count: numDevices,
}),
continueText: _t("Sign out devices", { count: numDevices }),
continueText: _t("settings|sessions|confirm_sign_out_continue", { count: numDevices }),
continueKind: "danger",
},
};

View file

@ -341,7 +341,7 @@ export default class RolesRoomSettingsTab extends React.Component<IProps> {
parseIntWithDefault(plContent.events_default, powerLevelDescriptors.events_default.defaultValue),
);
let privilegedUsersSection = <div>{_t("No users have specific privileges in this room")}</div>;
let privilegedUsersSection = <div>{_t("room_settings|permissions|no_privileged_users")}</div>;
let mutedUsersSection;
if (Object.keys(userLevels).length) {
const privilegedUsers: JSX.Element[] = [];
@ -391,11 +391,17 @@ export default class RolesRoomSettingsTab extends React.Component<IProps> {
if (privilegedUsers.length) {
privilegedUsersSection = (
<SettingsFieldset legend={_t("Privileged Users")}>{privilegedUsers}</SettingsFieldset>
<SettingsFieldset legend={_t("room_settings|permissions|privileged_users_section")}>
{privilegedUsers}
</SettingsFieldset>
);
}
if (mutedUsers.length) {
mutedUsersSection = <SettingsFieldset legend={_t("Muted Users")}>{mutedUsers}</SettingsFieldset>;
mutedUsersSection = (
<SettingsFieldset legend={_t("room_settings|permissions|muted_users_section")}>
{mutedUsers}
</SettingsFieldset>
);
}
}
@ -404,7 +410,7 @@ export default class RolesRoomSettingsTab extends React.Component<IProps> {
if (banned?.length) {
const canBanUsers = currentUserLevel >= banLevel;
bannedUsersSection = (
<SettingsFieldset legend={_t("Banned users")}>
<SettingsFieldset legend={_t("room_settings|permissions|banned_users_section")}>
<ul className="mx_RolesRoomSettingsTab_bannedList">
{banned.map((member) => {
const banEvent = member.events.member?.getContent();
@ -468,7 +474,7 @@ export default class RolesRoomSettingsTab extends React.Component<IProps> {
const brand = SdkConfig.get("element_call").brand ?? DEFAULTS.element_call.brand;
label = _t(translationKeyForEvent, { brand });
} else {
label = _t("Send %(eventType)s events", { eventType });
label = _t("room_settings|permissions|send_event_type", { eventType });
}
return (
<div key={eventType}>
@ -487,17 +493,17 @@ export default class RolesRoomSettingsTab extends React.Component<IProps> {
return (
<SettingsTab>
<SettingsSection heading={_t("Roles & Permissions")}>
<SettingsSection heading={_t("room_settings|permissions|title")}>
{privilegedUsersSection}
{canChangeLevels && <AddPrivilegedUsers room={room} defaultUserLevel={defaultUserLevel} />}
{mutedUsersSection}
{bannedUsersSection}
<SettingsFieldset
legend={_t("Permissions")}
legend={_t("room_settings|permissions|permissions_section")}
description={
isSpaceRoom
? _t("Select the roles required to change various parts of the space")
: _t("Select the roles required to change various parts of the room")
? _t("room_settings|permissions|permissions_section_description_space")
: _t("room_settings|permissions|permissions_section_description_room")
}
>
{powerSelectors}

View file

@ -116,13 +116,13 @@ export default class SecurityRoomSettingsTab extends React.Component<IProps, ISt
private onEncryptionChange = async (): Promise<void> => {
if (this.props.room.getJoinRule() === JoinRule.Public) {
const dialog = Modal.createDialog(QuestionDialog, {
title: _t("Are you sure you want to add encryption to this public room?"),
title: _t("room_settings|security|enable_encryption_public_room_confirm_title"),
description: (
<div>
<p>
{" "}
{_t(
"<b>It's not recommended to add encryption to public rooms.</b> Anyone can find and join public rooms, so anyone can read messages in them. You'll get none of the benefits of encryption, and you won't be able to turn it off later. Encrypting messages in a public room will make receiving and sending messages slower.",
"room_settings|security|enable_encryption_public_room_confirm_description_1",
undefined,
{ b: (sub) => <b>{sub}</b> },
)}{" "}
@ -130,7 +130,7 @@ export default class SecurityRoomSettingsTab extends React.Component<IProps, ISt
<p>
{" "}
{_t(
"To avoid these issues, create a <a>new encrypted room</a> for the conversation you plan to have.",
"room_settings|security|enable_encryption_public_room_confirm_description_2",
undefined,
{
a: (sub) => (
@ -158,9 +158,9 @@ export default class SecurityRoomSettingsTab extends React.Component<IProps, ISt
}
Modal.createDialog(QuestionDialog, {
title: _t("Enable encryption?"),
title: _t("room_settings|security|enable_encryption_confirm_title"),
description: _t(
"Once enabled, encryption for a room cannot be disabled. Messages sent in an encrypted room cannot be seen by the server, only by the participants of the room. Enabling encryption may prevent many bots and bridges from working correctly. <a>Learn more about encryption.</a>",
"room_settings|security|enable_encryption_confirm_description",
{},
{
a: (sub) => <ExternalLink href={SdkConfig.get("help_encryption_url")}>{sub}</ExternalLink>,
@ -259,11 +259,11 @@ export default class SecurityRoomSettingsTab extends React.Component<IProps, ISt
aliasWarning = (
<div className="mx_SecurityRoomSettingsTab_warning">
<WarningIcon width={15} height={15} />
<span>{_t("To link to this room, please add an address.")}</span>
<span>{_t("room_settings|security|public_without_alias_warning")}</span>
</div>
);
}
const description = _t("Decide who can join %(roomName)s.", {
const description = _t("room_settings|security|join_rule_description", {
roomName: room.name,
});
@ -309,37 +309,31 @@ export default class SecurityRoomSettingsTab extends React.Component<IProps, ISt
private onBeforeJoinRuleChange = async (joinRule: JoinRule): Promise<boolean> => {
if (this.state.encrypted && joinRule === JoinRule.Public) {
const dialog = Modal.createDialog(QuestionDialog, {
title: _t("Are you sure you want to make this encrypted room public?"),
title: _t("room_settings|security|encrypted_room_public_confirm_title"),
description: (
<div>
<p>
{" "}
{_t(
"<b>It's not recommended to make encrypted rooms public.</b> It will mean anyone can find and join the room, so anyone can read messages. You'll get none of the benefits of encryption. Encrypting messages in a public room will make receiving and sending messages slower.",
undefined,
{ b: (sub) => <b>{sub}</b> },
)}{" "}
{_t("room_settings|security|encrypted_room_public_confirm_description_1", undefined, {
b: (sub) => <b>{sub}</b>,
})}{" "}
</p>
<p>
{" "}
{_t(
"To avoid these issues, create a <a>new public room</a> for the conversation you plan to have.",
undefined,
{
a: (sub) => (
<AccessibleButton
kind="link_inline"
onClick={(): void => {
dialog.close();
this.createNewRoom(true, false);
}}
>
{" "}
{sub}{" "}
</AccessibleButton>
),
},
)}{" "}
{_t("room_settings|security|encrypted_room_public_confirm_description_2", undefined, {
a: (sub) => (
<AccessibleButton
kind="link_inline"
onClick={(): void => {
dialog.close();
this.createNewRoom(true, false);
}}
>
{" "}
{sub}{" "}
</AccessibleButton>
),
})}{" "}
</p>
</div>
),
@ -366,15 +360,15 @@ export default class SecurityRoomSettingsTab extends React.Component<IProps, ISt
const options = [
{
value: HistoryVisibility.Shared,
label: _t("Members only (since the point in time of selecting this option)"),
label: _t("room_settings|security|history_visibility_shared"),
},
{
value: HistoryVisibility.Invited,
label: _t("Members only (since they were invited)"),
label: _t("room_settings|security|history_visibility_invited"),
},
{
value: HistoryVisibility.Joined,
label: _t("Members only (since they joined)"),
label: _t("room_settings|security|history_visibility_joined"),
},
];
@ -382,16 +376,14 @@ export default class SecurityRoomSettingsTab extends React.Component<IProps, ISt
if (!this.state.encrypted || history === HistoryVisibility.WorldReadable) {
options.unshift({
value: HistoryVisibility.WorldReadable,
label: _t("Anyone"),
label: _t("room_settings|security|history_visibility_world_readable"),
});
}
const description = _t(
"Changes to who can read history will only apply to future messages in this room. The visibility of existing history will be unchanged.",
);
const description = _t("room_settings|security|history_visibility_warning");
return (
<SettingsFieldset legend={_t("Who can read history?")} description={description}>
<SettingsFieldset legend={_t("room_settings|security|history_visibility_legend")} description={description}>
<StyledRadioGroup
name="historyVis"
value={history}
@ -421,11 +413,7 @@ export default class SecurityRoomSettingsTab extends React.Component<IProps, ISt
disabled={!canSetGuestAccess}
label={_t("Enable guest access")}
/>
<p>
{_t(
"People with supported clients will be able to join the room without having a registered account.",
)}
</p>
<p>{_t("room_settings|security|guest_access_warning")}</p>
</div>
);
}
@ -454,13 +442,13 @@ export default class SecurityRoomSettingsTab extends React.Component<IProps, ISt
return (
<SettingsTab>
<SettingsSection heading={_t("Security & Privacy")}>
<SettingsSection heading={_t("room_settings|security|title")}>
<SettingsFieldset
legend={_t("Encryption")}
description={
isEncryptionForceDisabled && !isEncrypted
? undefined
: _t("Once enabled, encryption cannot be disabled.")
: _t("room_settings|security|encryption_permanent")
}
>
<LabelledToggleSwitch
@ -470,7 +458,7 @@ export default class SecurityRoomSettingsTab extends React.Component<IProps, ISt
disabled={!canEnableEncryption}
/>
{isEncryptionForceDisabled && !isEncrypted && (
<Caption>{_t("Your server requires encryption to be disabled.")}</Caption>
<Caption>{_t("room_settings|security|encryption_forced")}</Caption>
)}
{encryptionSettings}
</SettingsFieldset>