Merge branch 'develop' into andybalaam/fix-receipt-flakes

This commit is contained in:
Andy Balaam 2023-09-26 15:40:41 +01:00 committed by GitHub
commit 63c391a3f1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
203 changed files with 11748 additions and 10106 deletions

View file

@ -174,11 +174,15 @@ jobs:
record: true record: true
parallel: true parallel: true
command-prefix: "yarn percy exec --parallel --" command-prefix: "yarn percy exec --parallel --"
command: "npx cypress-cloud run"
config: '{"reporter":"cypress-multi-reporters", "reporterOptions": { "configFile": "cypress-ci-reporter-config.json" } }' config: '{"reporter":"cypress-multi-reporters", "reporterOptions": { "configFile": "cypress-ci-reporter-config.json" } }'
ci-build-id: ${{ needs.prepare.outputs.uuid }} ci-build-id: ${{ needs.prepare.outputs.uuid }}
env: env:
# pass the Dashboard record key as an environment variable # pass the Dashboard record key as an environment variable
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
CURRENTS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
CURRENTS_PROJECT_ID: ${{ github.repository }}
CURRENTS_API_URL: ${{ vars.CURRENTS_API_URL }}
# Use existing chromium rather than downloading another # Use existing chromium rather than downloading another
PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: true PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: true

View file

@ -1,3 +1,27 @@
Changes in [3.81.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.81.0) (2023-09-26)
=====================================================================================================
## ✨ Features
* Make video & voice call buttons pin conference widget if unpinned ([\#11576](https://github.com/matrix-org/matrix-react-sdk/pull/11576)). Fixes vector-im/customer-retainer#72.
* OIDC: persist refresh token ([\#11249](https://github.com/matrix-org/matrix-react-sdk/pull/11249)). Contributed by @kerryarchibald.
* ElementR: Cross user verification ([\#11364](https://github.com/matrix-org/matrix-react-sdk/pull/11364)). Fixes vector-im/element-web#25752. Contributed by @florianduros.
* Default intentional mentions ([\#11602](https://github.com/matrix-org/matrix-react-sdk/pull/11602)).
* Notify users about denied access on ask-to-join rooms ([\#11480](https://github.com/matrix-org/matrix-react-sdk/pull/11480)). Contributed by @nurjinjafar.
* Allow setting knock room directory visibility ([\#11529](https://github.com/matrix-org/matrix-react-sdk/pull/11529)). Contributed by @charlynguyen.
## 🐛 Bug Fixes
* Revert "Fix regression around FacePile with overflow (#11527)" ([\#11634](https://github.com/matrix-org/matrix-react-sdk/pull/11634)). Fixes vector-im/element-web#26209.
* Escape placeholder before injecting it into the style ([\#11607](https://github.com/matrix-org/matrix-react-sdk/pull/11607)).
* Move ViewUser action callback to RoomView ([\#11495](https://github.com/matrix-org/matrix-react-sdk/pull/11495)). Fixes vector-im/element-web#26040.
* Fix room timeline search toggling behaviour edge case ([\#11605](https://github.com/matrix-org/matrix-react-sdk/pull/11605)). Fixes vector-im/element-web#26105.
* Avoid rendering view-message link in RoomKnocksBar unnecessarily ([\#11598](https://github.com/matrix-org/matrix-react-sdk/pull/11598)). Contributed by @charlynguyen.
* Use knock rooms sync to reflect the knock state ([\#11596](https://github.com/matrix-org/matrix-react-sdk/pull/11596)). Fixes vector-im/element-web#26043 and vector-im/element-web#26044. Contributed by @charlynguyen.
* Fix avatar in right panel not using the correct font ([\#11593](https://github.com/matrix-org/matrix-react-sdk/pull/11593)). Fixes vector-im/element-web#26061. Contributed by @MidhunSureshR.
* Add waits in Spotlight Cypress tests, hoping this unflakes them ([\#11590](https://github.com/matrix-org/matrix-react-sdk/pull/11590)). Fixes vector-im/element-web#26053, vector-im/element-web#26140 vector-im/element-web#26139 and vector-im/element-web#26138. Contributed by @andybalaam.
* Fix vertical alignment of default avatar font ([\#11582](https://github.com/matrix-org/matrix-react-sdk/pull/11582)). Fixes vector-im/element-web#26081.
* Fix avatars in public room & space search being flex shrunk ([\#11580](https://github.com/matrix-org/matrix-react-sdk/pull/11580)). Fixes vector-im/element-web#26133.
* Fix EventTile avatars being rendered with a size of 0 instead of hidden ([\#11558](https://github.com/matrix-org/matrix-react-sdk/pull/11558)). Fixes vector-im/element-web#26075.
Changes in [3.80.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.80.1) (2023-09-13) Changes in [3.80.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.80.1) (2023-09-13)
===================================================================================================== =====================================================================================================

View file

@ -17,6 +17,8 @@ limitations under the License.
import { defineConfig } from "cypress"; import { defineConfig } from "cypress";
import * as fs from "node:fs"; import * as fs from "node:fs";
import registerPlugins from "./cypress/plugins";
export default defineConfig({ export default defineConfig({
video: true, video: true,
projectId: "ppvnzg", projectId: "ppvnzg",
@ -38,7 +40,7 @@ export default defineConfig({
} }
}); });
return require("./cypress/plugins/index.ts").default(on, config); return registerPlugins(on, config);
}, },
baseUrl: "http://localhost:8080", baseUrl: "http://localhost:8080",
specPattern: "cypress/e2e/**/*.spec.{js,jsx,ts,tsx}", specPattern: "cypress/e2e/**/*.spec.{js,jsx,ts,tsx}",

View file

@ -16,6 +16,8 @@ limitations under the License.
/// <reference types="cypress" /> /// <reference types="cypress" />
import installLogsPrinter from "cypress-terminal-report/src/installLogsPrinter"; import installLogsPrinter from "cypress-terminal-report/src/installLogsPrinter";
import cloudPlugin from "cypress-cloud/plugin";
import { initPlugins } from "cypress-plugin-init";
import PluginEvents = Cypress.PluginEvents; import PluginEvents = Cypress.PluginEvents;
import PluginConfigOptions = Cypress.PluginConfigOptions; import PluginConfigOptions = Cypress.PluginConfigOptions;
@ -32,15 +34,22 @@ import { mailhogDocker } from "./mailhog";
* @type {Cypress.PluginConfig} * @type {Cypress.PluginConfig}
*/ */
export default function (on: PluginEvents, config: PluginConfigOptions) { export default function (on: PluginEvents, config: PluginConfigOptions) {
docker(on, config); initPlugins(
synapseDocker(on, config); on,
dendriteDocker(on, config); [
slidingSyncProxyDocker(on, config); cloudPlugin,
webserver(on, config); docker,
oAuthServer(on, config); synapseDocker,
log(on, config); dendriteDocker,
slidingSyncProxyDocker,
webserver,
oAuthServer,
log,
mailhogDocker,
],
config,
);
installLogsPrinter(on, { installLogsPrinter(on, {
// printLogsToConsole: "always", // printLogsToConsole: "always",
}); });
mailhogDocker(on, config);
} }

View file

@ -20,6 +20,7 @@ import "@percy/cypress";
import "cypress-real-events"; import "cypress-real-events";
import "@testing-library/cypress/add-commands"; import "@testing-library/cypress/add-commands";
import installLogsCollector from "cypress-terminal-report/src/installLogsCollector"; import installLogsCollector from "cypress-terminal-report/src/installLogsCollector";
import "cypress-cloud/support";
import "./config.json"; import "./config.json";
import "./homeserver"; import "./homeserver";

View file

@ -1,6 +1,6 @@
{ {
"name": "matrix-react-sdk", "name": "matrix-react-sdk",
"version": "3.80.1", "version": "3.81.0",
"description": "SDK for matrix.org using React", "description": "SDK for matrix.org using React",
"author": "matrix.org", "author": "matrix.org",
"repository": { "repository": {
@ -23,7 +23,7 @@
"package.json", "package.json",
".stylelintrc.js" ".stylelintrc.js"
], ],
"main": "./src/index.ts", "main": "./lib/index.ts",
"matrix_src_main": "./src/index.ts", "matrix_src_main": "./src/index.ts",
"matrix_lib_main": "./lib/index.ts", "matrix_lib_main": "./lib/index.ts",
"matrix_lib_typings": "./lib/index.d.ts", "matrix_lib_typings": "./lib/index.d.ts",
@ -102,7 +102,7 @@
"matrix-encrypt-attachment": "^1.0.3", "matrix-encrypt-attachment": "^1.0.3",
"matrix-events-sdk": "0.0.1", "matrix-events-sdk": "0.0.1",
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop", "matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop",
"matrix-widget-api": "^1.6.0", "matrix-widget-api": "^1.5.0",
"memoize-one": "^6.0.0", "memoize-one": "^6.0.0",
"minimist": "^1.2.5", "minimist": "^1.2.5",
"oidc-client-ts": "^2.2.4", "oidc-client-ts": "^2.2.4",
@ -188,7 +188,9 @@
"chokidar": "^3.5.1", "chokidar": "^3.5.1",
"cypress": "^13.0.0", "cypress": "^13.0.0",
"cypress-axe": "^1.0.0", "cypress-axe": "^1.0.0",
"cypress-cloud": "^2.0.0-beta.0",
"cypress-multi-reporters": "^1.6.1", "cypress-multi-reporters": "^1.6.1",
"cypress-plugin-init": "^0.0.8",
"cypress-real-events": "^1.7.1", "cypress-real-events": "^1.7.1",
"cypress-terminal-report": "^5.3.2", "cypress-terminal-report": "^5.3.2",
"eslint": "8.48.0", "eslint": "8.48.0",
@ -231,5 +233,6 @@
"outputDirectory": "coverage", "outputDirectory": "coverage",
"outputName": "jest-sonar-report.xml", "outputName": "jest-sonar-report.xml",
"relativePaths": true "relativePaths": true
} },
"typings": "./lib/index.d.ts"
} }

View file

@ -15,11 +15,13 @@ limitations under the License.
*/ */
.mx_FacePile_more { .mx_FacePile_more {
/* Needed to calculate the offset on the face pile */
--cpd-avatar-size: 28px;
position: relative; position: relative;
border-radius: 100%; border-radius: 100%;
width: 30px; width: 28px;
height: 30px; height: 28px;
background-color: $spacePanel-bg-color; background-color: $panels;
display: inline-block; display: inline-block;
&::before { &::before {

View file

@ -191,7 +191,7 @@ export default class ManageEventIndexDialog extends React.Component<IProps, ISta
<BaseDialog <BaseDialog
className="mx_ManageEventIndexDialog" className="mx_ManageEventIndexDialog"
onFinished={this.props.onFinished} onFinished={this.props.onFinished}
title={_t("Message search")} title={_t("settings|security|message_search_section")}
> >
{eventIndexingSettings} {eventIndexingSettings}
<DialogButtons <DialogButtons

View file

@ -803,7 +803,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
onClick={this.onCopyClick} onClick={this.onCopyClick}
disabled={this.state.phase === Phase.Storing} disabled={this.state.phase === Phase.Storing}
> >
{this.state.copied ? _t("Copied!") : _t("action|copy")} {this.state.copied ? _t("common|copied") : _t("action|copy")}
</AccessibleButton> </AccessibleButton>
</div> </div>
</div> </div>
@ -873,7 +873,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
private titleForPhase(phase: Phase): string { private titleForPhase(phase: Phase): string {
switch (phase) { switch (phase) {
case Phase.ChooseKeyPassphrase: case Phase.ChooseKeyPassphrase:
return _t("Set up Secure Backup"); return _t("encryption|set_up_toast_title");
case Phase.Migrate: case Phase.Migrate:
return _t("Upgrade your encryption"); return _t("Upgrade your encryption");
case Phase.Passphrase: case Phase.Passphrase:

View file

@ -126,7 +126,7 @@ export default class ExportE2eKeysDialog extends React.Component<IProps, IState>
if (this.unmounted) { if (this.unmounted) {
return; return;
} }
const msg = e.friendlyText || _t("Unknown error"); const msg = e.friendlyText || _t("error|unknown");
this.setState({ this.setState({
errStr: msg, errStr: msg,
phase: Phase.Edit, phase: Phase.Edit,

View file

@ -119,7 +119,7 @@ export default class ImportE2eKeysDialog extends React.Component<IProps, IState>
if (this.unmounted) { if (this.unmounted) {
return; return;
} }
const msg = e.friendlyText || _t("Unknown error"); const msg = e.friendlyText || _t("error|unknown");
this.setState({ this.setState({
errStr: msg, errStr: msg,
phase: Phase.Edit, phase: Phase.Edit,

View file

@ -30,7 +30,7 @@ export default class SpaceProvider extends RoomProvider {
} }
public getName(): string { public getName(): string {
return _t("Spaces"); return _t("common|spaces");
} }
public renderCompletions(completions: React.ReactNode[]): React.ReactNode { public renderCompletions(completions: React.ReactNode[]): React.ReactNode {

View file

@ -95,7 +95,7 @@ export default class NotificationPanel extends React.PureComponent<IProps, IStat
header={ header={
<div className="mx_BaseCard_header_title"> <div className="mx_BaseCard_header_title">
<Heading size="4" className="mx_BaseCard_header_title_heading"> <Heading size="4" className="mx_BaseCard_header_title_heading">
{_t("Notifications")} {_t("notifications|enable_prompt_toast_title")}
</Heading> </Heading>
</div> </div>
} }

View file

@ -410,7 +410,7 @@ export const joinRoom = async (cli: MatrixClient, hierarchy: RoomHierarchy, room
logger.warn("Got a non-MatrixError while joining room", err); logger.warn("Got a non-MatrixError while joining room", err);
SdkContextClass.instance.roomViewStore.showJoinRoomError( SdkContextClass.instance.roomViewStore.showJoinRoomError(
new MatrixError({ new MatrixError({
error: _t("Unknown error"), error: _t("error|unknown"),
}), }),
roomId, roomId,
); );
@ -673,7 +673,7 @@ const ManageButtons: React.FC<IManageButtonsProps> = ({ hierarchy, selected, set
}; };
} }
let buttonText = _t("Saving…"); let buttonText = _t("common|saving");
if (!saving) { if (!saving) {
buttonText = selectionAllSuggested ? _t("space|unmark_suggested") : _t("space|mark_suggested"); buttonText = selectionAllSuggested ? _t("space|unmark_suggested") : _t("space|mark_suggested");
} }

View file

@ -303,8 +303,8 @@ const SpaceSetupFirstRooms: React.FC<{
const [busy, setBusy] = useState(false); const [busy, setBusy] = useState(false);
const [error, setError] = useState(""); const [error, setError] = useState("");
const numFields = 3; const numFields = 3;
const placeholders = [_t("General"), _t("common|random"), _t("common|support")]; const placeholders = [_t("common|general"), _t("common|random"), _t("common|support")];
const [roomNames, setRoomName] = useStateArray(numFields, [_t("General"), _t("common|random"), ""]); const [roomNames, setRoomName] = useStateArray(numFields, [_t("common|general"), _t("common|random"), ""]);
const fields = new Array(numFields).fill(0).map((x, i) => { const fields = new Array(numFields).fill(0).map((x, i) => {
const name = "roomName" + i; const name = "roomName" + i;
return ( return (

View file

@ -349,7 +349,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
{homeButton} {homeButton}
<IconizedContextMenuOption <IconizedContextMenuOption
iconClassName="mx_UserMenu_iconBell" iconClassName="mx_UserMenu_iconBell"
label={_t("Notifications")} label={_t("notifications|enable_prompt_toast_title")}
onClick={(e) => this.onSettingsOpen(e, UserTab.Notifications)} onClick={(e) => this.onSettingsOpen(e, UserTab.Notifications)}
/> />
<IconizedContextMenuOption <IconizedContextMenuOption
@ -359,7 +359,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
/> />
<IconizedContextMenuOption <IconizedContextMenuOption
iconClassName="mx_UserMenu_iconSettings" iconClassName="mx_UserMenu_iconSettings"
label={_t("All settings")} label={_t("user_menu|settings")}
onClick={(e) => this.onSettingsOpen(e)} onClick={(e) => this.onSettingsOpen(e)}
/> />
{feedbackButton} {feedbackButton}

View file

@ -679,7 +679,7 @@ export default class Registration extends React.Component<IProps, IState> {
<Fragment> <Fragment>
<div className="mx_Register_mainContent"> <div className="mx_Register_mainContent">
<AuthHeaderDisplay <AuthHeaderDisplay
title={_t("Create account")} title={_t("auth|create_account_title")}
serverPicker={ serverPicker={
<ServerPicker <ServerPicker
title={_t("auth|server_picker_title_registration")} title={_t("auth|server_picker_title_registration")}

View file

@ -99,7 +99,7 @@ export default function MemberAvatar({
} }
: props.onClick : props.onClick
} }
altText={_t("Profile picture")} altText={_t("common|user_avatar")}
/> />
); );
} }

View file

@ -477,7 +477,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
const viewSourceButton = ( const viewSourceButton = (
<IconizedContextMenuOption <IconizedContextMenuOption
iconClassName="mx_MessageContextMenu_iconSource" iconClassName="mx_MessageContextMenu_iconSource"
label={_t("View source")} label={_t("timeline|context_menu|view_source")}
onClick={this.onViewSourceClick} onClick={this.onViewSourceClick}
/> />
); );
@ -487,7 +487,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
unhidePreviewButton = ( unhidePreviewButton = (
<IconizedContextMenuOption <IconizedContextMenuOption
iconClassName="mx_MessageContextMenu_iconUnhidePreview" iconClassName="mx_MessageContextMenu_iconUnhidePreview"
label={_t("Show preview")} label={_t("timeline|context_menu|show_url_preview")}
onClick={this.onUnhidePreviewClick} onClick={this.onUnhidePreviewClick}
/> />
); );
@ -546,7 +546,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
<IconizedContextMenuOption <IconizedContextMenuOption
iconClassName="mx_MessageContextMenu_iconLink" iconClassName="mx_MessageContextMenu_iconLink"
onClick={this.closeMenu} onClick={this.closeMenu}
label={_t("Source URL")} label={_t("timeline|context_menu|external_url")}
element="a" element="a"
{ {
// XXX: Typescript signature for AccessibleButton doesn't work properly for non-inputs like `a` // XXX: Typescript signature for AccessibleButton doesn't work properly for non-inputs like `a`
@ -565,7 +565,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
collapseReplyChainButton = ( collapseReplyChainButton = (
<IconizedContextMenuOption <IconizedContextMenuOption
iconClassName="mx_MessageContextMenu_iconCollapse" iconClassName="mx_MessageContextMenu_iconCollapse"
label={_t("Collapse reply thread")} label={_t("timeline|context_menu|collapse_reply_thread")}
onClick={this.onCollapseReplyChainClick} onClick={this.onCollapseReplyChainClick}
/> />
); );
@ -577,7 +577,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
jumpToRelatedEventButton = ( jumpToRelatedEventButton = (
<IconizedContextMenuOption <IconizedContextMenuOption
iconClassName="mx_MessageContextMenu_jumpToEvent" iconClassName="mx_MessageContextMenu_jumpToEvent"
label={_t("View related event")} label={_t("timeline|context_menu|view_related_event")}
onClick={() => this.onJumpToRelatedEventClick(relatedEventId)} onClick={() => this.onJumpToRelatedEventClick(relatedEventId)}
/> />
); );
@ -588,7 +588,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
reportEventButton = ( reportEventButton = (
<IconizedContextMenuOption <IconizedContextMenuOption
iconClassName="mx_MessageContextMenu_iconReport" iconClassName="mx_MessageContextMenu_iconReport"
label={_t("Report")} label={_t("timeline|context_menu|report")}
onClick={this.onReportEventClick} onClick={this.onReportEventClick}
/> />
); );

View file

@ -83,7 +83,7 @@ const RoomContextMenu: React.FC<IProps> = ({ room, onFinished, ...props }) => {
leaveOption = ( leaveOption = (
<IconizedContextMenuOption <IconizedContextMenuOption
iconClassName="mx_RoomTile_iconSignOut" iconClassName="mx_RoomTile_iconSignOut"
label={_t("Forget")} label={_t("room|context_menu|forget")}
className="mx_IconizedContextMenu_option_red" className="mx_IconizedContextMenu_option_red"
onClick={onForgetRoomClick} onClick={onForgetRoomClick}
/> />
@ -154,7 +154,7 @@ const RoomContextMenu: React.FC<IProps> = ({ room, onFinished, ...props }) => {
PosthogTrackers.trackInteraction("WebRoomHeaderContextMenuFavouriteToggle", e); PosthogTrackers.trackInteraction("WebRoomHeaderContextMenuFavouriteToggle", e);
}} }}
active={isFavorite} active={isFavorite}
label={isFavorite ? _t("Favourited") : _t("Favourite")} label={isFavorite ? _t("room|context_menu|unfavourite") : _t("room|context_menu|favourite")}
iconClassName="mx_RoomTile_iconStar" iconClassName="mx_RoomTile_iconStar"
/> />
); );
@ -182,7 +182,7 @@ const RoomContextMenu: React.FC<IProps> = ({ room, onFinished, ...props }) => {
iconClassName = "mx_RoomTile_iconNotificationsAllMessages"; iconClassName = "mx_RoomTile_iconNotificationsAllMessages";
break; break;
case RoomNotifState.MentionsOnly: case RoomNotifState.MentionsOnly:
notificationLabel = _t("Mentions only"); notificationLabel = _t("room|context_menu|mentions_only");
iconClassName = "mx_RoomTile_iconNotificationsMentionsKeywords"; iconClassName = "mx_RoomTile_iconNotificationsMentionsKeywords";
break; break;
case RoomNotifState.Mute: case RoomNotifState.Mute:
@ -206,7 +206,7 @@ const RoomContextMenu: React.FC<IProps> = ({ room, onFinished, ...props }) => {
PosthogTrackers.trackInteraction("WebRoomHeaderContextMenuNotificationsItem", ev); PosthogTrackers.trackInteraction("WebRoomHeaderContextMenuNotificationsItem", ev);
}} }}
label={_t("Notifications")} label={_t("notifications|enable_prompt_toast_title")}
iconClassName={iconClassName} iconClassName={iconClassName}
> >
<span className="mx_IconizedContextMenu_sublabel">{notificationLabel}</span> <span className="mx_IconizedContextMenu_sublabel">{notificationLabel}</span>
@ -247,7 +247,7 @@ const RoomContextMenu: React.FC<IProps> = ({ room, onFinished, ...props }) => {
}); });
onFinished(); onFinished();
}} }}
label={_t("Copy room link")} label={_t("room|context_menu|copy_link")}
iconClassName="mx_RoomTile_iconCopyLink" iconClassName="mx_RoomTile_iconCopyLink"
/> />
); );

View file

@ -111,7 +111,7 @@ export const RoomGeneralContextMenu: React.FC<RoomGeneralContextMenuProps> = ({
<IconizedContextMenuCheckbox <IconizedContextMenuCheckbox
onClick={wrapHandler((ev) => onTagRoom(ev, DefaultTagID.Favourite), onPostFavoriteClick, true)} onClick={wrapHandler((ev) => onTagRoom(ev, DefaultTagID.Favourite), onPostFavoriteClick, true)}
active={isFavorite} active={isFavorite}
label={isFavorite ? _t("Favourited") : _t("Favourite")} label={isFavorite ? _t("room|context_menu|unfavourite") : _t("room|context_menu|favourite")}
iconClassName="mx_RoomGeneralContextMenu_iconStar" iconClassName="mx_RoomGeneralContextMenu_iconStar"
/> />
); );
@ -121,7 +121,7 @@ export const RoomGeneralContextMenu: React.FC<RoomGeneralContextMenuProps> = ({
<IconizedContextMenuCheckbox <IconizedContextMenuCheckbox
onClick={wrapHandler((ev) => onTagRoom(ev, DefaultTagID.LowPriority), onPostLowPriorityClick, true)} onClick={wrapHandler((ev) => onTagRoom(ev, DefaultTagID.LowPriority), onPostLowPriorityClick, true)}
active={isLowPriority} active={isLowPriority}
label={_t("Low Priority")} label={_t("room|context_menu|low_priority")}
iconClassName="mx_RoomGeneralContextMenu_iconArrowDown" iconClassName="mx_RoomGeneralContextMenu_iconArrowDown"
/> />
); );
@ -156,7 +156,7 @@ export const RoomGeneralContextMenu: React.FC<RoomGeneralContextMenuProps> = ({
}), }),
onPostCopyLinkClick, onPostCopyLinkClick,
)} )}
label={_t("Copy room link")} label={_t("room|context_menu|copy_link")}
iconClassName="mx_RoomGeneralContextMenu_iconCopyLink" iconClassName="mx_RoomGeneralContextMenu_iconCopyLink"
/> />
); );
@ -182,7 +182,7 @@ export const RoomGeneralContextMenu: React.FC<RoomGeneralContextMenuProps> = ({
leaveOption = ( leaveOption = (
<IconizedContextMenuOption <IconizedContextMenuOption
iconClassName="mx_RoomGeneralContextMenu_iconSignOut" iconClassName="mx_RoomGeneralContextMenu_iconSignOut"
label={_t("Forget Room")} label={_t("room|context_menu|forget")}
className="mx_IconizedContextMenu_option_red" className="mx_IconizedContextMenu_option_red"
onClick={wrapHandler( onClick={wrapHandler(
() => () =>
@ -221,7 +221,7 @@ export const RoomGeneralContextMenu: React.FC<RoomGeneralContextMenuProps> = ({
onFinished?.(); onFinished?.();
}} }}
active={false} active={false}
label={_t("Mark as read")} label={_t("room|context_menu|mark_read")}
iconClassName="mx_RoomGeneralContextMenu_iconMarkAsRead" iconClassName="mx_RoomGeneralContextMenu_iconMarkAsRead"
/> />
) : null; ) : null;

View file

@ -52,7 +52,7 @@ export const RoomNotificationContextMenu: React.FC<IProps> = ({ room, onFinished
const defaultOption: JSX.Element = ( const defaultOption: JSX.Element = (
<IconizedContextMenuRadio <IconizedContextMenuRadio
label={_t("Match default setting")} label={_t("room|context_menu|notifications_default")}
active={notificationState === RoomNotifState.AllMessages} active={notificationState === RoomNotifState.AllMessages}
iconClassName="mx_RoomNotificationContextMenu_iconBell" iconClassName="mx_RoomNotificationContextMenu_iconBell"
onClick={wrapHandler(() => setNotificationState(RoomNotifState.AllMessages))} onClick={wrapHandler(() => setNotificationState(RoomNotifState.AllMessages))}
@ -70,7 +70,7 @@ export const RoomNotificationContextMenu: React.FC<IProps> = ({ room, onFinished
const mentionsOption: JSX.Element = ( const mentionsOption: JSX.Element = (
<IconizedContextMenuRadio <IconizedContextMenuRadio
label={_t("Mentions & keywords")} label={_t("notifications|mentions_keywords")}
active={notificationState === RoomNotifState.MentionsOnly} active={notificationState === RoomNotifState.MentionsOnly}
iconClassName="mx_RoomNotificationContextMenu_iconBellMentions" iconClassName="mx_RoomNotificationContextMenu_iconBellMentions"
onClick={wrapHandler(() => setNotificationState(RoomNotifState.MentionsOnly))} onClick={wrapHandler(() => setNotificationState(RoomNotifState.MentionsOnly))}
@ -79,7 +79,7 @@ export const RoomNotificationContextMenu: React.FC<IProps> = ({ room, onFinished
const muteOption: JSX.Element = ( const muteOption: JSX.Element = (
<IconizedContextMenuRadio <IconizedContextMenuRadio
label={_t("Mute room")} label={_t("room|context_menu|notifications_mute")}
active={notificationState === RoomNotifState.Mute} active={notificationState === RoomNotifState.Mute}
iconClassName="mx_RoomNotificationContextMenu_iconBellCrossed" iconClassName="mx_RoomNotificationContextMenu_iconBellCrossed"
onClick={wrapHandler(() => setNotificationState(RoomNotifState.Mute))} onClick={wrapHandler(() => setNotificationState(RoomNotifState.Mute))}

View file

@ -144,7 +144,10 @@ export const WidgetContextMenu: React.FC<IProps> = ({
onFinished(); onFinished();
}; };
streamAudioStreamButton = ( streamAudioStreamButton = (
<IconizedContextMenuOption onClick={onStreamAudioClick} label={_t("Start audio stream")} /> <IconizedContextMenuOption
onClick={onStreamAudioClick}
label={_t("widget|context_menu|start_audio_stream")}
/>
); );
} }
@ -179,7 +182,9 @@ export const WidgetContextMenu: React.FC<IProps> = ({
onFinished(); onFinished();
}; };
snapshotButton = <IconizedContextMenuOption onClick={onSnapshotClick} label={_t("Take a picture")} />; snapshotButton = (
<IconizedContextMenuOption onClick={onSnapshotClick} label={_t("widget|context_menu|screenshot")} />
);
} }
let deleteButton: JSX.Element | undefined; let deleteButton: JSX.Element | undefined;
@ -190,11 +195,9 @@ export const WidgetContextMenu: React.FC<IProps> = ({
} else if (roomId) { } else if (roomId) {
// Show delete confirmation dialog // Show delete confirmation dialog
Modal.createDialog(QuestionDialog, { Modal.createDialog(QuestionDialog, {
title: _t("Delete Widget"), title: _t("widget|context_menu|delete"),
description: _t( description: _t("widget|context_menu|delete_warning"),
"Deleting a widget removes it for all users in this room. Are you sure you want to delete this widget?", button: _t("widget|context_menu|delete"),
),
button: _t("Delete widget"),
onFinished: (confirmed) => { onFinished: (confirmed) => {
if (!confirmed) return; if (!confirmed) return;
WidgetUtils.setRoomWidget(cli, roomId, app.id); WidgetUtils.setRoomWidget(cli, roomId, app.id);
@ -208,7 +211,7 @@ export const WidgetContextMenu: React.FC<IProps> = ({
deleteButton = ( deleteButton = (
<IconizedContextMenuOption <IconizedContextMenuOption
onClick={_onDeleteClick} onClick={_onDeleteClick}
label={userWidget ? _t("action|remove") : _t("Remove for everyone")} label={userWidget ? _t("action|remove") : _t("widget|context_menu|remove")}
/> />
); );
} }
@ -233,7 +236,9 @@ export const WidgetContextMenu: React.FC<IProps> = ({
onFinished(); onFinished();
}; };
revokeButton = <IconizedContextMenuOption onClick={onRevokeClick} label={_t("Revoke permissions")} />; revokeButton = (
<IconizedContextMenuOption onClick={onRevokeClick} label={_t("widget|context_menu|revoke")} />
);
} }
} }
@ -246,7 +251,7 @@ export const WidgetContextMenu: React.FC<IProps> = ({
onFinished(); onFinished();
}; };
moveLeftButton = <IconizedContextMenuOption onClick={onClick} label={_t("Move left")} />; moveLeftButton = <IconizedContextMenuOption onClick={onClick} label={_t("widget|context_menu|move_left")} />;
} }
let moveRightButton: JSX.Element | undefined; let moveRightButton: JSX.Element | undefined;
@ -257,7 +262,7 @@ export const WidgetContextMenu: React.FC<IProps> = ({
onFinished(); onFinished();
}; };
moveRightButton = <IconizedContextMenuOption onClick={onClick} label={_t("Move right")} />; moveRightButton = <IconizedContextMenuOption onClick={onClick} label={_t("widget|context_menu|move_right")} />;
} }
return ( return (

View file

@ -388,7 +388,7 @@ const defaultRendererFactory =
); );
export const defaultRoomsRenderer = defaultRendererFactory(_td("Rooms")); export const defaultRoomsRenderer = defaultRendererFactory(_td("Rooms"));
export const defaultSpacesRenderer = defaultRendererFactory(_td("Spaces")); export const defaultSpacesRenderer = defaultRendererFactory(_td("common|spaces"));
export const defaultDmsRenderer = defaultRendererFactory(_td("Direct Messages")); export const defaultDmsRenderer = defaultRendererFactory(_td("Direct Messages"));
interface ISubspaceSelectorProps { interface ISubspaceSelectorProps {
@ -494,7 +494,7 @@ const AddExistingToSpaceDialog: React.FC<IProps> = ({ space, onCreateRoomClick,
roomsRenderer={defaultRoomsRenderer} roomsRenderer={defaultRoomsRenderer}
spacesRenderer={() => ( spacesRenderer={() => (
<div className="mx_AddExistingToSpace_section"> <div className="mx_AddExistingToSpace_section">
<h3>{_t("Spaces")}</h3> <h3>{_t("common|spaces")}</h3>
<AccessibleButton <AccessibleButton
kind="link" kind="link"
onClick={() => { onClick={() => {

View file

@ -61,7 +61,7 @@ export default class ChangelogDialog extends React.Component<IProps, State> {
const body = await res.json(); const body = await res.json();
this.setState({ [repo]: body.commits }); this.setState({ [repo]: body.commits });
} catch (err) { } catch (err) {
this.setState({ [repo]: err instanceof Error ? err.message : _t("Unknown error") }); this.setState({ [repo]: err instanceof Error ? err.message : _t("error|unknown") });
} }
} }

View file

@ -334,7 +334,7 @@ export default class CreateRoomDialog extends React.Component<IProps, IState> {
visibilitySection = ( visibilitySection = (
<LabelledCheckbox <LabelledCheckbox
className="mx_CreateRoomDialog_labelledCheckbox" className="mx_CreateRoomDialog_labelledCheckbox"
label={_t("Make this room visible in the public room directory.")} label={_t("room_settings|security|publish_room")}
onChange={this.onIsPublicKnockRoomChange} onChange={this.onIsPublicKnockRoomChange}
value={this.state.isPublicKnockRoom} value={this.state.isPublicKnockRoom}
/> />
@ -417,7 +417,9 @@ export default class CreateRoomDialog extends React.Component<IProps, IState> {
<JoinRuleDropdown <JoinRuleDropdown
label={_t("create_room|room_visibility_label")} label={_t("create_room|room_visibility_label")}
labelInvite={_t("create_room|join_rule_invite")} labelInvite={_t("create_room|join_rule_invite")}
labelKnock={this.askToJoinEnabled ? _t("Ask to join") : undefined} labelKnock={
this.askToJoinEnabled ? _t("room_settings|security|join_rule_knock") : undefined
}
labelPublic={_t("Public room")} labelPublic={_t("Public room")}
labelRestricted={ labelRestricted={
this.supportsRestricted ? _t("create_room|join_rule_restricted") : undefined this.supportsRestricted ? _t("create_room|join_rule_restricted") : undefined
@ -432,7 +434,7 @@ export default class CreateRoomDialog extends React.Component<IProps, IState> {
{aliasField} {aliasField}
<details onToggle={this.onDetailsToggled} className="mx_CreateRoomDialog_details"> <details onToggle={this.onDetailsToggled} className="mx_CreateRoomDialog_details">
<summary className="mx_CreateRoomDialog_details_summary"> <summary className="mx_CreateRoomDialog_details_summary">
{this.state.detailsOpen ? _t("Hide advanced") : _t("Show advanced")} {this.state.detailsOpen ? _t("action|hide_advanced") : _t("action|show_advanced")}
</summary> </summary>
<LabelledToggleSwitch <LabelledToggleSwitch
label={_t("create_room|unfederated", { label={_t("create_room|unfederated", {

View file

@ -182,7 +182,7 @@ export default class LogoutDialog extends React.Component<IProps, IState> {
let setupButtonCaption; let setupButtonCaption;
if (this.state.backupStatus === BackupStatus.SERVER_BACKUP_BUT_DISABLED) { if (this.state.backupStatus === BackupStatus.SERVER_BACKUP_BUT_DISABLED) {
setupButtonCaption = _t("Connect this session to Key Backup"); setupButtonCaption = _t("settings|security|key_backup_connect");
} 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
@ -203,7 +203,7 @@ export default class LogoutDialog extends React.Component<IProps, IState> {
<button onClick={this.onLogoutConfirm}>{_t("I don't want my encrypted messages")}</button> <button onClick={this.onLogoutConfirm}>{_t("I don't want my encrypted messages")}</button>
</DialogButtons> </DialogButtons>
<details> <details>
<summary>{_t("Advanced")}</summary> <summary>{_t("common|advanced")}</summary>
<p> <p>
<button onClick={this.onExportE2eKeysClicked}>{_t("Manually export keys")}</button> <button onClick={this.onExportE2eKeysClicked}>{_t("Manually export keys")}</button>
</p> </p>

View file

@ -161,7 +161,7 @@ export default class MessageEditHistoryDialog extends React.PureComponent<IProps
); );
} 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("Something went wrong!")}</p>; content = <p className="mx_MessageEditHistoryDialog_error">{_t("error|something_went_wrong")}</p>;
} else { } else {
content = ( content = (
<p className="mx_MessageEditHistoryDialog_error"> <p className="mx_MessageEditHistoryDialog_error">

View file

@ -134,7 +134,7 @@ class RoomSettingsDialog extends React.Component<IProps, IState> {
tabs.push( tabs.push(
new Tab( new Tab(
RoomSettingsTab.General, RoomSettingsTab.General,
_td("General"), _td("common|general"),
"mx_RoomSettingsDialog_settingsIcon", "mx_RoomSettingsDialog_settingsIcon",
<GeneralRoomSettingsTab room={this.state.room} />, <GeneralRoomSettingsTab room={this.state.room} />,
"RoomSettingsGeneral", "RoomSettingsGeneral",
@ -181,7 +181,7 @@ class RoomSettingsDialog extends React.Component<IProps, IState> {
tabs.push( tabs.push(
new Tab( new Tab(
RoomSettingsTab.Notifications, RoomSettingsTab.Notifications,
_td("Notifications"), _td("notifications|enable_prompt_toast_title"),
"mx_RoomSettingsDialog_notificationsIcon", "mx_RoomSettingsDialog_notificationsIcon",
( (
<NotificationSettingsTab <NotificationSettingsTab
@ -218,7 +218,7 @@ class RoomSettingsDialog extends React.Component<IProps, IState> {
tabs.push( tabs.push(
new Tab( new Tab(
RoomSettingsTab.Advanced, RoomSettingsTab.Advanced,
_td("Advanced"), _td("common|advanced"),
"mx_RoomSettingsDialog_warningIcon", "mx_RoomSettingsDialog_warningIcon",
( (
<AdvancedRoomSettingsTab <AdvancedRoomSettingsTab

View file

@ -55,13 +55,13 @@ const SpaceSettingsDialog: React.FC<IProps> = ({ matrixClient: cli, space, onFin
return [ return [
new Tab( new Tab(
SpaceSettingsTab.General, SpaceSettingsTab.General,
_td("General"), _td("common|general"),
"mx_SpaceSettingsDialog_generalIcon", "mx_SpaceSettingsDialog_generalIcon",
<SpaceSettingsGeneralTab matrixClient={cli} space={space} />, <SpaceSettingsGeneralTab matrixClient={cli} space={space} />,
), ),
new Tab( new Tab(
SpaceSettingsTab.Visibility, SpaceSettingsTab.Visibility,
_td("Visibility"), _td("room_settings|visibility|title"),
"mx_SpaceSettingsDialog_visibilityIcon", "mx_SpaceSettingsDialog_visibilityIcon",
<SpaceSettingsVisibilityTab matrixClient={cli} space={space} closeSettingsFn={onFinished} />, <SpaceSettingsVisibilityTab matrixClient={cli} space={space} closeSettingsFn={onFinished} />,
), ),
@ -74,7 +74,7 @@ const SpaceSettingsDialog: React.FC<IProps> = ({ matrixClient: cli, space, onFin
SettingsStore.getValue(UIFeature.AdvancedSettings) SettingsStore.getValue(UIFeature.AdvancedSettings)
? new Tab( ? new Tab(
SpaceSettingsTab.Advanced, SpaceSettingsTab.Advanced,
_td("Advanced"), _td("common|advanced"),
"mx_RoomSettingsDialog_warningIcon", "mx_RoomSettingsDialog_warningIcon",
<AdvancedRoomSettingsTab room={space} closeSettingsFn={onFinished} />, <AdvancedRoomSettingsTab room={space} closeSettingsFn={onFinished} />,
) )

View file

@ -77,7 +77,7 @@ export default class UserSettingsDialog extends React.Component<IProps, IState>
tabs.push( tabs.push(
new Tab( new Tab(
UserTab.General, UserTab.General,
_td("General"), _td("common|general"),
"mx_UserSettingsDialog_settingsIcon", "mx_UserSettingsDialog_settingsIcon",
<GeneralUserSettingsTab closeSettingsFn={this.props.onFinished} />, <GeneralUserSettingsTab closeSettingsFn={this.props.onFinished} />,
"UserSettingsGeneral", "UserSettingsGeneral",
@ -95,7 +95,7 @@ export default class UserSettingsDialog extends React.Component<IProps, IState>
tabs.push( tabs.push(
new Tab( new Tab(
UserTab.Notifications, UserTab.Notifications,
_td("Notifications"), _td("notifications|enable_prompt_toast_title"),
"mx_UserSettingsDialog_bellIcon", "mx_UserSettingsDialog_bellIcon",
<NotificationUserSettingsTab />, <NotificationUserSettingsTab />,
"UserSettingsNotifications", "UserSettingsNotifications",
@ -122,7 +122,7 @@ export default class UserSettingsDialog extends React.Component<IProps, IState>
tabs.push( tabs.push(
new Tab( new Tab(
UserTab.Sidebar, UserTab.Sidebar,
_td("Sidebar"), _td("settings|sidebar|title"),
"mx_UserSettingsDialog_sidebarIcon", "mx_UserSettingsDialog_sidebarIcon",
<SidebarUserSettingsTab />, <SidebarUserSettingsTab />,
"UserSettingsSidebar", "UserSettingsSidebar",
@ -153,7 +153,7 @@ export default class UserSettingsDialog extends React.Component<IProps, IState>
tabs.push( tabs.push(
new Tab( new Tab(
UserTab.SessionManager, UserTab.SessionManager,
_td("Sessions"), _td("settings|sessions|title"),
"mx_UserSettingsDialog_sessionsIcon", "mx_UserSettingsDialog_sessionsIcon",
<SessionManagerTab />, <SessionManagerTab />,
// don't track with posthog while under construction // don't track with posthog while under construction

View file

@ -63,7 +63,7 @@ export default class SetupEncryptionDialog extends React.Component<IProps, IStat
<BaseDialog <BaseDialog
headerImage={this.state.icon} headerImage={this.state.icon}
onFinished={this.props.onFinished} onFinished={this.props.onFinished}
title={_t("Verify this session")} title={_t("encryption|verify_toast_title")}
> >
<SetupEncryptionBody onFinished={this.props.onFinished} /> <SetupEncryptionBody onFinished={this.props.onFinished} />
</BaseDialog> </BaseDialog>

View file

@ -92,7 +92,7 @@ export function RoomResultContextMenus({ room }: Props): JSX.Element {
const target = ev.target as HTMLElement; const target = ev.target as HTMLElement;
setGeneralMenuPosition(target.getBoundingClientRect()); setGeneralMenuPosition(target.getBoundingClientRect());
}} }}
title={room.isSpaceRoom() ? _t("Space options") : _t("Room options")} title={room.isSpaceRoom() ? _t("space|context_menu|options") : _t("Room options")}
isExpanded={generalMenuPosition !== null} isExpanded={generalMenuPosition !== null}
/> />
)} )}

View file

@ -934,7 +934,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
copyPlaintext(ownInviteLink); copyPlaintext(ownInviteLink);
}} }}
onHideTooltip={() => setInviteLinkCopied(false)} onHideTooltip={() => setInviteLinkCopied(false)}
title={inviteLinkCopied ? _t("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("Copy invite link")}

View file

@ -101,17 +101,17 @@ export default class AppPermission extends React.Component<IProps, IState> {
const warningTooltipText = ( const warningTooltipText = (
<div> <div>
{_t("Any of the following data may be shared:")} {_t("analytics|shared_data_heading")}
<ul> <ul>
<li>{_t("Your display name")}</li> <li>{_t("widget|shared_data_name")}</li>
<li>{_t("Your profile picture URL")}</li> <li>{_t("widget|shared_data_avatar")}</li>
<li>{_t("Your user ID")}</li> <li>{_t("widget|shared_data_mxid")}</li>
<li>{_t("Your device ID")}</li> <li>{_t("widget|shared_data_device_id")}</li>
<li>{_t("Your theme")}</li> <li>{_t("widget|shared_data_theme")}</li>
<li>{_t("Your language")}</li> <li>{_t("widget|shared_data_lang")}</li>
<li>{_t("%(brand)s URL", { brand })}</li> <li>{_t("widget|shared_data_url", { brand })}</li>
<li>{_t("Room ID")}</li> <li>{_t("widget|shared_data_room_id")}</li>
<li>{_t("Widget ID")}</li> <li>{_t("widget|shared_data_widget_id")}</li>
</ul> </ul>
</div> </div>
); );
@ -128,22 +128,22 @@ export default class AppPermission extends React.Component<IProps, IState> {
// Due to i18n limitations, we can't dedupe the code for variables in these two messages. // Due to i18n limitations, we can't dedupe the code for variables in these two messages.
const warning = this.state.isWrapped const warning = this.state.isWrapped
? _t( ? _t(
"Using this widget may share data <helpIcon /> with %(widgetDomain)s & your integration manager.", "widget|shared_data_warning_im",
{ widgetDomain: this.state.widgetDomain }, { widgetDomain: this.state.widgetDomain },
{ helpIcon: () => warningTooltip }, { helpIcon: () => warningTooltip },
) )
: _t( : _t(
"Using this widget may share data <helpIcon /> with %(widgetDomain)s.", "widget|shared_data_warning",
{ widgetDomain: this.state.widgetDomain }, { widgetDomain: this.state.widgetDomain },
{ helpIcon: () => warningTooltip }, { helpIcon: () => warningTooltip },
); );
const encryptionWarning = this.props.isRoomEncrypted ? _t("Widgets do not use message encryption.") : null; const encryptionWarning = this.props.isRoomEncrypted ? _t("widget|unencrypted_warning") : null;
return ( return (
<div className="mx_AppPermission"> <div className="mx_AppPermission">
<div className="mx_AppPermission_content"> <div className="mx_AppPermission_content">
<div className="mx_AppPermission_content_bolder">{_t("Widget added by")}</div> <div className="mx_AppPermission_content_bolder">{_t("widget|added_by")}</div>
<div> <div>
{avatar} {avatar}
<Heading size="4">{displayName}</Heading> <Heading size="4">{displayName}</Heading>
@ -151,7 +151,7 @@ export default class AppPermission extends React.Component<IProps, IState> {
</div> </div>
<div>{warning}</div> <div>{warning}</div>
<div> <div>
{_t("This widget may use cookies.")}&nbsp;{encryptionWarning} {_t("widget|cookie_warning")}&nbsp;{encryptionWarning}
</div> </div>
<div> <div>
<AccessibleButton kind="primary_sm" onClick={this.props.onPermissionGranted}> <AccessibleButton kind="primary_sm" onClick={this.props.onPermissionGranted}>

View file

@ -629,7 +629,7 @@ export default class AppTile extends React.Component<IProps, IState> {
if (this.sgWidget === null) { if (this.sgWidget === null) {
appTileBody = ( appTileBody = (
<div className={appTileBodyClass} style={appTileBodyStyles}> <div className={appTileBodyClass} style={appTileBodyStyles}>
<AppWarning errorMsg={_t("Error loading Widget")} /> <AppWarning errorMsg={_t("widget|error_loading")} />
</div> </div>
); );
} else if (!this.state.hasPermissionToLoad && this.props.room) { } else if (!this.state.hasPermissionToLoad && this.props.room) {
@ -656,7 +656,7 @@ export default class AppTile extends React.Component<IProps, IState> {
if (this.isMixedContent()) { if (this.isMixedContent()) {
appTileBody = ( appTileBody = (
<div className={appTileBodyClass} style={appTileBodyStyles}> <div className={appTileBodyClass} style={appTileBodyStyles}>
<AppWarning errorMsg={_t("Error - Mixed content")} /> <AppWarning errorMsg={_t("widget|error_mixed_content")} />
</div> </div>
); );
} else { } else {
@ -737,7 +737,7 @@ export default class AppTile extends React.Component<IProps, IState> {
<AccessibleButton <AccessibleButton
key="toggleMaximised" key="toggleMaximised"
className="mx_AppTileMenuBar_widgets_button" className="mx_AppTileMenuBar_widgets_button"
title={isMaximised ? _t("Un-maximise") : _t("action|maximise")} title={isMaximised ? _t("widget|unmaximise") : _t("action|maximise")}
onClick={this.onToggleMaximisedClick} onClick={this.onToggleMaximisedClick}
> >
{isMaximised ? ( {isMaximised ? (
@ -776,7 +776,7 @@ export default class AppTile extends React.Component<IProps, IState> {
{this.props.showPopout && !this.state.requiresClient && ( {this.props.showPopout && !this.state.requiresClient && (
<AccessibleButton <AccessibleButton
className="mx_AppTileMenuBar_widgets_button" className="mx_AppTileMenuBar_widgets_button"
title={_t("Popout widget")} title={_t("widget|popout")}
onClick={this.onPopoutWidgetClick} onClick={this.onPopoutWidgetClick}
> >
<PopoutIcon className="mx_Icon mx_Icon_12 mx_Icon--stroke" /> <PopoutIcon className="mx_Icon mx_Icon_12 mx_Icon--stroke" />

View file

@ -37,7 +37,7 @@ const CopyableText: React.FC<IProps> = ({ children, getTextToCopy, border = true
e.preventDefault(); e.preventDefault();
const text = getTextToCopy(); const text = getTextToCopy();
const successful = !!text && (await copyPlaintext(text)); const successful = !!text && (await copyPlaintext(text));
setTooltip(successful ? _t("Copied!") : _t("Failed to copy")); setTooltip(successful ? _t("common|copied") : _t("error|failed_copy"));
}; };
const onHideTooltip = (): void => { const onHideTooltip = (): void => {

View file

@ -154,15 +154,15 @@ export default class DesktopCapturerSourcePicker extends React.Component<PickerI
public render(): React.ReactNode { public render(): React.ReactNode {
const tabs: NonEmptyArray<Tab<TabId>> = [ const tabs: NonEmptyArray<Tab<TabId>> = [
this.getTab("screen", _td("Share entire screen")), this.getTab("screen", _td("voip|screenshare_monitor")),
this.getTab("window", _td("Application window")), this.getTab("window", _td("voip|screenshare_window")),
]; ];
return ( return (
<BaseDialog <BaseDialog
className="mx_desktopCapturerSourcePicker" className="mx_desktopCapturerSourcePicker"
onFinished={this.onCloseClick} onFinished={this.onCloseClick}
title={_t("Share content")} title={_t("voip|screenshare_title")}
> >
<TabbedView tabs={tabs} tabLocation={TabLocation.TOP} onChange={this.onTabChange} /> <TabbedView tabs={tabs} tabLocation={TabLocation.TOP} onChange={this.onTabChange} />
<DialogButtons <DialogButtons

View file

@ -123,7 +123,7 @@ export default class ErrorBoundary extends React.PureComponent<Props, IState> {
return ( return (
<div className="mx_ErrorBoundary"> <div className="mx_ErrorBoundary">
<div className="mx_ErrorBoundary_body"> <div className="mx_ErrorBoundary_body">
<h1>{_t("Something went wrong!")}</h1> <h1>{_t("error|something_went_wrong")}</h1>
{bugReportSection} {bugReportSection}
{clearCacheButton} {clearCacheButton}
</div> </div>

View file

@ -57,9 +57,8 @@ const FacePile: FC<IProps> = ({
const pileContents = ( const pileContents = (
<> <>
{/* XXX: The margin-left is a workaround for Compound's styling excluding this element and being overly specific */}
{overflow ? <span className="mx_FacePile_more" style={{ marginLeft: `calc(${size} * -0.2)` }} /> : null}
{faces} {faces}
{overflow ? <span className="mx_FacePile_more" /> : null}
</> </>
); );

View file

@ -542,7 +542,7 @@ export default class ImageView extends React.Component<IProps, IState> {
lockProps={{ lockProps={{
"onKeyDown": this.onKeyDown, "onKeyDown": this.onKeyDown,
"role": "dialog", "role": "dialog",
"aria-label": _t("Image view"), "aria-label": _t("lightbox|title"),
}} }}
className="mx_ImageView" className="mx_ImageView"
ref={this.focusLock} ref={this.focusLock}
@ -555,12 +555,12 @@ export default class ImageView extends React.Component<IProps, IState> {
{zoomInButton} {zoomInButton}
<AccessibleTooltipButton <AccessibleTooltipButton
className="mx_ImageView_button mx_ImageView_button_rotateCCW" className="mx_ImageView_button mx_ImageView_button_rotateCCW"
title={_t("Rotate Left")} title={_t("lightbox|rotate_left")}
onClick={this.onRotateCounterClockwiseClick} onClick={this.onRotateCounterClockwiseClick}
/> />
<AccessibleTooltipButton <AccessibleTooltipButton
className="mx_ImageView_button mx_ImageView_button_rotateCW" className="mx_ImageView_button mx_ImageView_button_rotateCW"
title={_t("Rotate Right")} title={_t("lightbox|rotate_right")}
onClick={this.onRotateClockwiseClick} onClick={this.onRotateClockwiseClick}
/> />
<AccessibleTooltipButton <AccessibleTooltipButton

View file

@ -79,8 +79,8 @@ export default class TagComposer extends React.PureComponent<IProps, IState> {
id={this.props.id ? this.props.id + "_field" : undefined} id={this.props.id ? this.props.id + "_field" : undefined}
value={this.state.newTag} value={this.state.newTag}
onChange={this.onInputChange} onChange={this.onInputChange}
label={this.props.label || _t("Keyword")} label={this.props.label || _t("notifications|keyword")}
placeholder={this.props.placeholder || _t("New keyword")} placeholder={this.props.placeholder || _t("notifications|keyword_new")}
disabled={this.props.disabled} disabled={this.props.disabled}
autoComplete="off" autoComplete="off"
/> />

View file

@ -32,18 +32,14 @@ export const EnableLiveShare: React.FC<Props> = ({ onSubmit }) => {
<div data-testid="location-picker-enable-live-share" className="mx_EnableLiveShare"> <div data-testid="location-picker-enable-live-share" className="mx_EnableLiveShare">
<StyledLiveBeaconIcon className="mx_EnableLiveShare_icon" /> <StyledLiveBeaconIcon className="mx_EnableLiveShare_icon" />
<Heading className="mx_EnableLiveShare_heading" size="3"> <Heading className="mx_EnableLiveShare_heading" size="3">
{_t("Live location sharing")} {_t("location_sharing|live_enable_heading")}
</Heading> </Heading>
<p className="mx_EnableLiveShare_description"> <p className="mx_EnableLiveShare_description">{_t("location_sharing|live_enable_description")}</p>
{_t(
"Please note: this is a labs feature using a temporary implementation. This means you will not be able to delete your location history, and advanced users will be able to see your location history even after you stop sharing your live location with this room.",
)}
</p>
<LabelledToggleSwitch <LabelledToggleSwitch
data-testid="enable-live-share-toggle" data-testid="enable-live-share-toggle"
value={isEnabled} value={isEnabled}
onChange={setEnabled} onChange={setEnabled}
label={_t("Enable live location sharing")} label={_t("location_sharing|live_toggle_label")}
/> />
<AccessibleButton <AccessibleButton
data-testid="enable-live-share-submit" data-testid="enable-live-share-submit"

View file

@ -35,7 +35,7 @@ interface Props {
} }
const getLabel = (durationMs: number): string => { const getLabel = (durationMs: number): string => {
return _t("Share for %(duration)s", { duration: formatDuration(durationMs) }); return _t("location_sharing|live_share_button", { duration: formatDuration(durationMs) });
}; };
const LiveDurationDropdown: React.FC<Props> = ({ timeout, onChange }) => { const LiveDurationDropdown: React.FC<Props> = ({ timeout, onChange }) => {

View file

@ -224,7 +224,11 @@ class LocationPicker extends React.Component<ILocationPickerProps, IState> {
{this.props.shareType === LocationShareType.Pin && ( {this.props.shareType === LocationShareType.Pin && (
<div className="mx_LocationPicker_pinText"> <div className="mx_LocationPicker_pinText">
<span>{this.state.position ? _t("Click to move the pin") : _t("Click to drop a pin")}</span> <span>
{this.state.position
? _t("location_sharing|click_move_pin")
: _t("location_sharing|click_drop_pin")}
</span>
</div> </div>
)} )}
<div className="mx_LocationPicker_footer"> <div className="mx_LocationPicker_footer">
@ -241,7 +245,7 @@ class LocationPicker extends React.Component<ILocationPickerProps, IState> {
disabled={!this.state.position} disabled={!this.state.position}
onClick={this.onOk} onClick={this.onOk}
> >
{_t("Share location")} {_t("location_sharing|share_button")}
</AccessibleButton> </AccessibleButton>
</form> </form>
</div> </div>

View file

@ -250,7 +250,7 @@ export default class LegacyCallEvent extends React.PureComponent<IProps, IState>
if (this.state.callState === CallState.Connecting) { if (this.state.callState === CallState.Connecting) {
return ( return (
<div className="mx_LegacyCallEvent_content"> <div className="mx_LegacyCallEvent_content">
{_t("Connecting")} {_t("voip|connecting")}
{this.props.timestamp} {this.props.timestamp}
</div> </div>
); );

View file

@ -196,7 +196,7 @@ export default class TextualBody extends React.Component<IBodyProps, IState> {
const { close } = ContextMenu.createMenu(GenericTextContextMenu, { const { close } = ContextMenu.createMenu(GenericTextContextMenu, {
...toRightOf(buttonRect, 0), ...toRightOf(buttonRect, 0),
chevronFace: ChevronFace.None, chevronFace: ChevronFace.None,
message: successful ? _t("Copied!") : _t("Failed to copy"), message: successful ? _t("common|copied") : _t("error|failed_copy"),
}); });
button.onmouseleave = close; button.onmouseleave = close;
}; };

View file

@ -288,7 +288,7 @@ export default class LegacyRoomHeaderButtons extends HeaderButtons<IProps> {
<HeaderButton <HeaderButton
key="notifsButton" key="notifsButton"
name="notifsButton" name="notifsButton"
title={_t("Notifications")} title={_t("notifications|enable_prompt_toast_title")}
isHighlighted={this.isPhase(RightPanelPhases.NotificationPanel)} isHighlighted={this.isPhase(RightPanelPhases.NotificationPanel)}
onClick={this.onNotificationsClicked} onClick={this.onNotificationsClicked}
isUnread={this.globalNotificationState.color === NotificationColor.Red} isUnread={this.globalNotificationState.color === NotificationColor.Red}

View file

@ -1163,7 +1163,7 @@ export const PowerLevelEditor: React.FC<{
logger.error("Failed to change power level " + err); logger.error("Failed to change power level " + err);
Modal.createDialog(ErrorDialog, { Modal.createDialog(ErrorDialog, {
title: _t("common|error"), title: _t("common|error"),
description: _t("Failed to change power level"), description: _t("error|update_power_level"),
}); });
}, },
); );

View file

@ -737,7 +737,7 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
switch (this.state.shieldReason) { switch (this.state.shieldReason) {
case null: case null:
case EventShieldReason.UNKNOWN: case EventShieldReason.UNKNOWN:
shieldReasonMessage = _t("Unknown error"); shieldReasonMessage = _t("error|unknown");
break; break;
case EventShieldReason.UNVERIFIED_IDENTITY: case EventShieldReason.UNVERIFIED_IDENTITY:

View file

@ -290,26 +290,24 @@ const CallButtons: FC<CallButtonsProps> = ({ room }) => {
} else if (groupCallsEnabled) { } else if (groupCallsEnabled) {
if (useElementCallExclusively) { if (useElementCallExclusively) {
if (hasGroupCall) { if (hasGroupCall) {
return makeVideoCallButton(new DisabledWithReason(_t("Ongoing call"))); return makeVideoCallButton(new DisabledWithReason(_t("voip|disabled_ongoing_call")));
} else if (mayCreateElementCalls) { } else if (mayCreateElementCalls) {
return makeVideoCallButton("element"); return makeVideoCallButton("element");
} else { } else {
return makeVideoCallButton( return makeVideoCallButton(new DisabledWithReason(_t("voip|disabled_no_perms_start_video_call")));
new DisabledWithReason(_t("You do not have permission to start video calls")),
);
} }
} else if (hasLegacyCall || hasJitsiWidget || hasGroupCall) { } else if (hasLegacyCall || hasJitsiWidget || hasGroupCall) {
return ( return (
<> <>
{makeVoiceCallButton(new DisabledWithReason(_t("Ongoing call")))} {makeVoiceCallButton(new DisabledWithReason(_t("voip|disabled_ongoing_call")))}
{makeVideoCallButton(new DisabledWithReason(_t("Ongoing call")))} {makeVideoCallButton(new DisabledWithReason(_t("voip|disabled_ongoing_call")))}
</> </>
); );
} else if (functionalMembers.length <= 1) { } else if (functionalMembers.length <= 1) {
return ( return (
<> <>
{makeVoiceCallButton(new DisabledWithReason(_t("There's no one here to call")))} {makeVoiceCallButton(new DisabledWithReason(_t("voip|disabled_no_one_here")))}
{makeVideoCallButton(new DisabledWithReason(_t("There's no one here to call")))} {makeVideoCallButton(new DisabledWithReason(_t("voip|disabled_no_one_here")))}
</> </>
); );
} else if (functionalMembers.length === 2) { } else if (functionalMembers.length === 2) {
@ -329,10 +327,10 @@ const CallButtons: FC<CallButtonsProps> = ({ room }) => {
} else { } else {
const videoCallBehavior = mayCreateElementCalls const videoCallBehavior = mayCreateElementCalls
? "element" ? "element"
: new DisabledWithReason(_t("You do not have permission to start video calls")); : new DisabledWithReason(_t("voip|disabled_no_perms_start_video_call"));
return ( return (
<> <>
{makeVoiceCallButton(new DisabledWithReason(_t("You do not have permission to start voice calls")))} {makeVoiceCallButton(new DisabledWithReason(_t("voip|disabled_no_perms_start_voice_call")))}
{makeVideoCallButton(videoCallBehavior)} {makeVideoCallButton(videoCallBehavior)}
</> </>
); );
@ -340,15 +338,15 @@ const CallButtons: FC<CallButtonsProps> = ({ room }) => {
} else if (hasLegacyCall || hasJitsiWidget) { } else if (hasLegacyCall || hasJitsiWidget) {
return ( return (
<> <>
{makeVoiceCallButton(new DisabledWithReason(_t("Ongoing call")))} {makeVoiceCallButton(new DisabledWithReason(_t("voip|disabled_ongoing_call")))}
{makeVideoCallButton(new DisabledWithReason(_t("Ongoing call")))} {makeVideoCallButton(new DisabledWithReason(_t("voip|disabled_ongoing_call")))}
</> </>
); );
} else if (functionalMembers.length <= 1) { } else if (functionalMembers.length <= 1) {
return ( return (
<> <>
{makeVoiceCallButton(new DisabledWithReason(_t("There's no one here to call")))} {makeVoiceCallButton(new DisabledWithReason(_t("voip|disabled_no_one_here")))}
{makeVideoCallButton(new DisabledWithReason(_t("There's no one here to call")))} {makeVideoCallButton(new DisabledWithReason(_t("voip|disabled_no_one_here")))}
</> </>
); );
} else if (functionalMembers.length === 2 || mayEditWidgets) { } else if (functionalMembers.length === 2 || mayEditWidgets) {
@ -361,8 +359,8 @@ const CallButtons: FC<CallButtonsProps> = ({ room }) => {
} else { } else {
return ( return (
<> <>
{makeVoiceCallButton(new DisabledWithReason(_t("You do not have permission to start voice calls")))} {makeVoiceCallButton(new DisabledWithReason(_t("voip|disabled_no_perms_start_voice_call")))}
{makeVideoCallButton(new DisabledWithReason(_t("You do not have permission to start video calls")))} {makeVideoCallButton(new DisabledWithReason(_t("voip|disabled_no_perms_start_video_call")))}
</> </>
); );
} }
@ -764,7 +762,7 @@ export default class RoomHeader extends React.Component<IProps, IState> {
const buttons = this.props.showButtons ? this.renderButtons(isVideoRoom) : null; const buttons = this.props.showButtons ? this.renderButtons(isVideoRoom) : null;
let oobName = _t("Join Room"); let oobName = _t("Unnamed room");
if (this.props.oobData && this.props.oobData.name) { if (this.props.oobData && this.props.oobData.name) {
oobName = this.props.oobData.name; oobName = this.props.oobData.name;
} }

View file

@ -131,7 +131,7 @@ const MessageComposerButtons: React.FC<IProps> = (props: IProps) => {
<AccessibleTooltipButton <AccessibleTooltipButton
className={moreOptionsClasses} className={moreOptionsClasses}
onClick={props.toggleButtonMenu} onClick={props.toggleButtonMenu}
title={_t("More options")} title={_t("quick_settings|sidebar_settings")}
/> />
)} )}
{props.isMenuOpen && ( {props.isMenuOpen && (

View file

@ -203,14 +203,14 @@ export default function RoomHeader({ room }: { room: Room }): JSX.Element {
</IconButton> </IconButton>
</Tooltip> </Tooltip>
{notificationsEnabled && ( {notificationsEnabled && (
<Tooltip label={_t("Notifications")}> <Tooltip label={_t("notifications|enable_prompt_toast_title")}>
<IconButton <IconButton
indicator={notificationColorToIndicator(globalNotificationState.color)} indicator={notificationColorToIndicator(globalNotificationState.color)}
onClick={(evt) => { onClick={(evt) => {
evt.stopPropagation(); evt.stopPropagation();
RightPanelStore.instance.showOrHidePanel(RightPanelPhases.NotificationPanel); RightPanelStore.instance.showOrHidePanel(RightPanelPhases.NotificationPanel);
}} }}
aria-label={_t("Notifications")} aria-label={_t("notifications|enable_prompt_toast_title")}
> >
<NotificationsIcon /> <NotificationsIcon />
</IconButton> </IconButton>

View file

@ -650,9 +650,9 @@ export default class RoomSublist extends React.Component<IProps, IState> {
{({ onFocus, isActive, ref }) => { {({ onFocus, isActive, ref }) => {
const tabIndex = isActive ? 0 : -1; const tabIndex = isActive ? 0 : -1;
let ariaLabel = _t("Jump to first unread room."); let ariaLabel = _t("a11y_jump_first_unread_room");
if (this.props.tagId === DefaultTagID.Invite) { if (this.props.tagId === DefaultTagID.Invite) {
ariaLabel = _t("Jump to first invite."); ariaLabel = _t("a11y|jump_first_invite");
} }
const badge = ( const badge = (

View file

@ -35,7 +35,7 @@ export default class TopUnreadMessagesBar extends React.PureComponent<IProps> {
/> />
<AccessibleButton <AccessibleButton
className="mx_TopUnreadMessagesBar_markAsRead" className="mx_TopUnreadMessagesBar_markAsRead"
title={_t("Mark all as read")} title={_t("notifications|mark_all_read")}
onClick={this.props.onCloseClick} onClick={this.props.onCloseClick}
/> />
</div> </div>

View file

@ -55,7 +55,7 @@ export const AddPrivilegedUsers: React.FC<AddPrivilegedUsersProps> = ({ room, de
if (powerLevelEvent === null) { if (powerLevelEvent === null) {
Modal.createDialog(ErrorDialog, { Modal.createDialog(ErrorDialog, {
title: _t("common|error"), title: _t("common|error"),
description: _t("Failed to change power level"), description: _t("error|update_power_level"),
}); });
return; return;
@ -68,7 +68,7 @@ export const AddPrivilegedUsers: React.FC<AddPrivilegedUsersProps> = ({ room, de
} catch (error) { } catch (error) {
Modal.createDialog(ErrorDialog, { Modal.createDialog(ErrorDialog, {
title: _t("common|error"), title: _t("common|error"),
description: _t("Failed to change power level"), description: _t("error|update_power_level"),
}); });
} finally { } finally {
setIsLoading(false); setIsLoading(false);
@ -78,13 +78,13 @@ export const AddPrivilegedUsers: React.FC<AddPrivilegedUsersProps> = ({ room, de
return ( return (
<form style={{ display: "flex" }} onSubmit={onSubmit}> <form style={{ display: "flex" }} onSubmit={onSubmit}>
<SettingsFieldset <SettingsFieldset
legend={_t("Add privileged users")} legend={_t("room_settings|permissions|add_privileged_user_heading")}
description={_t("Give one or multiple users in this room more privileges")} description={_t("room_settings|permissions|add_privileged_user_description")}
style={{ flexGrow: 1 }} style={{ flexGrow: 1 }}
> >
<AutocompleteInput <AutocompleteInput
provider={userProvider.current} provider={userProvider.current}
placeholder={_t("Search users in this room…")} placeholder={_t("room_settings|permissions|add_privileged_user_filter_placeholder")}
onSelectionChange={setSelectedUsers} onSelectionChange={setSelectedUsers}
selection={selectedUsers} selection={selectedUsers}
additionalFilter={hasLowerOrEqualLevelThanDefaultLevelFilter} additionalFilter={hasLowerOrEqualLevelThanDefaultLevelFilter}

View file

@ -42,7 +42,7 @@ export default class ChangeDisplayName extends React.Component {
return ( return (
<EditableTextContainer <EditableTextContainer
getInitialValue={this.getDisplayName} getInitialValue={this.getDisplayName}
placeholder={_t("No display name")} placeholder={_t("settings|general|name_placeholder")}
blurToSubmit={true} blurToSubmit={true}
onSubmit={this.changeDisplayName} onSubmit={this.changeDisplayName}
/> />

View file

@ -184,33 +184,31 @@ export default class CrossSigningPanel extends React.PureComponent<{}, IState> {
} else if (!homeserverSupportsCrossSigning) { } else if (!homeserverSupportsCrossSigning) {
summarisedStatus = ( summarisedStatus = (
<SettingsSubsectionText data-testid="summarised-status"> <SettingsSubsectionText data-testid="summarised-status">
{_t("Your homeserver does not support cross-signing.")} {_t("encryption|cross_signing_unsupported")}
</SettingsSubsectionText> </SettingsSubsectionText>
); );
} else if (crossSigningReady && crossSigningPrivateKeysInStorage) { } else if (crossSigningReady && crossSigningPrivateKeysInStorage) {
summarisedStatus = ( summarisedStatus = (
<SettingsSubsectionText data-testid="summarised-status"> <SettingsSubsectionText data-testid="summarised-status">
{_t("Cross-signing is ready for use.")} {_t("encryption|cross_signing_ready")}
</SettingsSubsectionText> </SettingsSubsectionText>
); );
} else if (crossSigningReady && !crossSigningPrivateKeysInStorage) { } else if (crossSigningReady && !crossSigningPrivateKeysInStorage) {
summarisedStatus = ( summarisedStatus = (
<SettingsSubsectionText data-testid="summarised-status"> <SettingsSubsectionText data-testid="summarised-status">
{_t("Cross-signing is ready but keys are not backed up.")} {_t("encryption|cross_signing_ready_no_backup")}
</SettingsSubsectionText> </SettingsSubsectionText>
); );
} else if (crossSigningPrivateKeysInStorage) { } else if (crossSigningPrivateKeysInStorage) {
summarisedStatus = ( summarisedStatus = (
<SettingsSubsectionText data-testid="summarised-status"> <SettingsSubsectionText data-testid="summarised-status">
{_t( {_t("encryption|cross_signing_untrusted")}
"Your account has a cross-signing identity in secret storage, but it is not yet trusted by this session.",
)}
</SettingsSubsectionText> </SettingsSubsectionText>
); );
} else { } else {
summarisedStatus = ( summarisedStatus = (
<SettingsSubsectionText data-testid="summarised-status"> <SettingsSubsectionText data-testid="summarised-status">
{_t("Cross-signing is not set up.")} {_t("encryption|cross_signing_not_ready")}
</SettingsSubsectionText> </SettingsSubsectionText>
); );
} }
@ -232,9 +230,9 @@ export default class CrossSigningPanel extends React.PureComponent<{}, IState> {
// TODO: determine how better to expose this to users in addition to prompts at login/toast // TODO: determine how better to expose this to users in addition to prompts at login/toast
if (!keysExistEverywhere && homeserverSupportsCrossSigning) { if (!keysExistEverywhere && homeserverSupportsCrossSigning) {
let buttonCaption = _t("Set up Secure Backup"); let buttonCaption = _t("encryption|set_up_toast_title");
if (crossSigningPrivateKeysInStorage) { if (crossSigningPrivateKeysInStorage) {
buttonCaption = _t("Verify this session"); buttonCaption = _t("encryption|verify_toast_title");
} }
actions.push( actions.push(
<AccessibleButton key="setup" kind="primary" onClick={this.onBootstrapClick}> <AccessibleButton key="setup" kind="primary" onClick={this.onBootstrapClick}>
@ -260,7 +258,7 @@ export default class CrossSigningPanel extends React.PureComponent<{}, IState> {
<> <>
{summarisedStatus} {summarisedStatus}
<details> <details>
<summary>{_t("Advanced")}</summary> <summary>{_t("common|advanced")}</summary>
<table className="mx_CrossSigningPanel_statusList"> <table className="mx_CrossSigningPanel_statusList">
<tr> <tr>
<th scope="row">{_t("settings|security|cross_signing_public_keys")}</th> <th scope="row">{_t("settings|security|cross_signing_public_keys")}</th>

View file

@ -42,7 +42,7 @@ export default class CryptographyPanel extends React.Component<IProps, IState> {
const deviceId = client.deviceId; const deviceId = client.deviceId;
let identityKey = client.getDeviceEd25519Key(); let identityKey = client.getDeviceEd25519Key();
if (!identityKey) { if (!identityKey) {
identityKey = _t("<not supported>"); identityKey = _t("encryption|not_supported");
} else { } else {
identityKey = FormattingUtils.formatCryptoKey(identityKey); identityKey = FormattingUtils.formatCryptoKey(identityKey);
} }

View file

@ -29,9 +29,7 @@ const E2eAdvancedPanel: React.FC = () => {
<SettingsSubsection heading={_t("settings|security|encryption_section")}> <SettingsSubsection heading={_t("settings|security|encryption_section")}>
<SettingsFlag name={SETTING_MANUALLY_VERIFY_ALL_SESSIONS} level={SettingLevel.DEVICE} /> <SettingsFlag name={SETTING_MANUALLY_VERIFY_ALL_SESSIONS} level={SettingLevel.DEVICE} />
<SettingsSubsectionText> <SettingsSubsectionText>
{_t( {_t("settings|security|encryption_individual_verification_mode")}
"Individually verify each session used by a user to mark it as trusted, not trusting cross-signed devices.",
)}
</SettingsSubsectionText> </SettingsSubsectionText>
</SettingsSubsection> </SettingsSubsection>
); );

View file

@ -148,16 +148,13 @@ export default class EventIndexPanel extends React.Component<{}, IState> {
eventIndexingSettings = ( eventIndexingSettings = (
<> <>
<SettingsSubsectionText> <SettingsSubsectionText>
{_t( {_t("settings|security|message_search_enabled", {
"Securely cache encrypted messages locally for them to appear in search results, using %(size)s to store messages from %(rooms)s rooms.", size: formatBytes(this.state.eventIndexSize, 0),
{ // This drives the singular / plural string
size: formatBytes(this.state.eventIndexSize, 0), // selection for "room" / "rooms" only.
// This drives the singular / plural string count: this.state.roomCount,
// selection for "room" / "rooms" only. rooms: formatCountLong(this.state.roomCount),
count: this.state.roomCount, })}
rooms: formatCountLong(this.state.roomCount),
},
)}
</SettingsSubsectionText> </SettingsSubsectionText>
<AccessibleButton kind="primary" onClick={this.onManage}> <AccessibleButton kind="primary" onClick={this.onManage}>
{_t("action|manage")} {_t("action|manage")}
@ -167,9 +164,7 @@ export default class EventIndexPanel extends React.Component<{}, IState> {
} else if (!this.state.eventIndexingEnabled && EventIndexPeg.supportIsInstalled()) { } else if (!this.state.eventIndexingEnabled && EventIndexPeg.supportIsInstalled()) {
eventIndexingSettings = ( eventIndexingSettings = (
<> <>
<SettingsSubsectionText> <SettingsSubsectionText>{_t("settings|security|message_search_disabled")}</SettingsSubsectionText>
{_t("Securely cache encrypted messages locally for them to appear in search results.")}
</SettingsSubsectionText>
<div> <div>
<AccessibleButton kind="primary" disabled={this.state.enabling} onClick={this.onEnable}> <AccessibleButton kind="primary" disabled={this.state.enabling} onClick={this.onEnable}>
{_t("action|enable")} {_t("action|enable")}
@ -187,7 +182,7 @@ export default class EventIndexPanel extends React.Component<{}, IState> {
eventIndexingSettings = ( eventIndexingSettings = (
<SettingsSubsectionText> <SettingsSubsectionText>
{_t( {_t(
"%(brand)s is missing some components required for securely caching encrypted messages locally. If you'd like to experiment with this feature, build a custom %(brand)s Desktop with <nativeLink>search components added</nativeLink>.", "settings|security|message_search_unsupported",
{ {
brand, brand,
}, },
@ -205,7 +200,7 @@ export default class EventIndexPanel extends React.Component<{}, IState> {
eventIndexingSettings = ( eventIndexingSettings = (
<SettingsSubsectionText> <SettingsSubsectionText>
{_t( {_t(
"%(brand)s can't securely cache encrypted messages locally while running in a web browser. Use <desktopLink>%(brand)s Desktop</desktopLink> for encrypted messages to appear in search results.", "settings|security|message_search_unsupported_web",
{ {
brand, brand,
}, },
@ -227,16 +222,16 @@ export default class EventIndexPanel extends React.Component<{}, IState> {
eventIndexingSettings = ( eventIndexingSettings = (
<> <>
<SettingsSubsectionText> <SettingsSubsectionText>
{this.state.enabling ? <InlineSpinner /> : _t("Message search initialisation failed")} {this.state.enabling ? <InlineSpinner /> : _t("settings|security|message_search_failed")}
</SettingsSubsectionText> </SettingsSubsectionText>
{EventIndexPeg.error && ( {EventIndexPeg.error && (
<SettingsSubsectionText> <SettingsSubsectionText>
<details> <details>
<summary>{_t("Advanced")}</summary> <summary>{_t("common|advanced")}</summary>
<code> <code>
{EventIndexPeg.error instanceof Error {EventIndexPeg.error instanceof Error
? EventIndexPeg.error.message ? EventIndexPeg.error.message
: _t("Unknown error")} : _t("error|unknown")}
</code> </code>
<p> <p>
<AccessibleButton key="delete" kind="danger" onClick={this.confirmEventStoreReset}> <AccessibleButton key="delete" kind="danger" onClick={this.confirmEventStoreReset}>

View file

@ -89,7 +89,7 @@ export default class IntegrationManager extends React.Component<IProps, IState>
if (this.props.loading) { if (this.props.loading) {
return ( return (
<div className="mx_IntegrationManager_loading"> <div className="mx_IntegrationManager_loading">
<Heading size="3">{_t("Connecting to integration manager…")}</Heading> <Heading size="3">{_t("integration_manager|connecting")}</Heading>
<Spinner /> <Spinner />
</div> </div>
); );
@ -98,8 +98,8 @@ export default class IntegrationManager extends React.Component<IProps, IState>
if (!this.props.connected || this.state.errored) { if (!this.props.connected || this.state.errored) {
return ( return (
<div className="mx_IntegrationManager_error"> <div className="mx_IntegrationManager_error">
<Heading size="3">{_t("Cannot connect to integration manager")}</Heading> <Heading size="3">{_t("integration_manager|error_connecting_heading")}</Heading>
<p>{_t("The integration manager is offline or it cannot reach your homeserver.")}</p> <p>{_t("integration_manager|error_connecting")}</p>
</div> </div>
); );
} }

View file

@ -131,15 +131,15 @@ const JoinRuleSettings: React.FC<JoinRuleSettingsProps> = ({
const roomId = await upgradeRoom(room, targetVersion, opts.invite, true, true, true, (progress) => { const roomId = await upgradeRoom(room, targetVersion, opts.invite, true, true, true, (progress) => {
const total = 2 + progress.updateSpacesTotal + progress.inviteUsersTotal; const total = 2 + progress.updateSpacesTotal + progress.inviteUsersTotal;
if (!progress.roomUpgraded) { if (!progress.roomUpgraded) {
fn(_t("Upgrading room"), 0, total); fn(_t("room_settings|security|join_rule_upgrade_upgrading_room"), 0, total);
} else if (!progress.roomSynced) { } else if (!progress.roomSynced) {
fn(_t("Loading new room"), 1, total); fn(_t("room_settings|security|join_rule_upgrade_awaiting_room"), 1, total);
} else if ( } else if (
progress.inviteUsersProgress !== undefined && progress.inviteUsersProgress !== undefined &&
progress.inviteUsersProgress < progress.inviteUsersTotal progress.inviteUsersProgress < progress.inviteUsersTotal
) { ) {
fn( fn(
_t("Sending invites... (%(progress)s out of %(count)s)", { _t("room_settings|security|join_rule_upgrade_sending_invites", {
progress: progress.inviteUsersProgress, progress: progress.inviteUsersProgress,
count: progress.inviteUsersTotal, count: progress.inviteUsersTotal,
}), }),
@ -151,7 +151,7 @@ const JoinRuleSettings: React.FC<JoinRuleSettingsProps> = ({
progress.updateSpacesProgress < progress.updateSpacesTotal progress.updateSpacesProgress < progress.updateSpacesTotal
) { ) {
fn( fn(
_t("Updating spaces... (%(progress)s out of %(count)s)", { _t("room_settings|security|join_rule_upgrade_updating_spaces", {
progress: progress.updateSpacesProgress, progress: progress.updateSpacesProgress,
count: progress.updateSpacesTotal, count: progress.updateSpacesTotal,
}), }),
@ -179,7 +179,11 @@ const JoinRuleSettings: React.FC<JoinRuleSettingsProps> = ({
}); });
}; };
const upgradeRequiredPill = <span className="mx_JoinRuleSettings_upgradeRequired">{_t("Upgrade required")}</span>; const upgradeRequiredPill = (
<span className="mx_JoinRuleSettings_upgradeRequired">
{_t("room_settings|security|join_rule_upgrade_required")}
</span>
);
const definitions: IDefinition<JoinRule>[] = [ const definitions: IDefinition<JoinRule>[] = [
{ {
@ -213,11 +217,11 @@ const JoinRuleSettings: React.FC<JoinRuleSettingsProps> = ({
let moreText; let moreText;
if (shownSpaces.length < restrictedAllowRoomIds.length) { if (shownSpaces.length < restrictedAllowRoomIds.length) {
if (shownSpaces.length > 0) { if (shownSpaces.length > 0) {
moreText = _t("& %(count)s more", { moreText = _t("room_settings|security|join_rule_restricted_n_more", {
count: restrictedAllowRoomIds.length - shownSpaces.length, count: restrictedAllowRoomIds.length - shownSpaces.length,
}); });
} else { } else {
moreText = _t("Currently, %(count)s spaces have access", { moreText = _t("room_settings|security|join_rule_restricted_summary", {
count: restrictedAllowRoomIds.length, count: restrictedAllowRoomIds.length,
}); });
} }
@ -256,7 +260,7 @@ const JoinRuleSettings: React.FC<JoinRuleSettingsProps> = ({
<div> <div>
<span> <span>
{_t( {_t(
"Anyone in a space can find and join. <a>Edit which spaces can access here.</a>", "room_settings|security|join_rule_restricted_description",
{}, {},
{ {
a: (sub) => ( a: (sub) => (
@ -273,7 +277,7 @@ const JoinRuleSettings: React.FC<JoinRuleSettingsProps> = ({
</span> </span>
<div className="mx_JoinRuleSettings_spacesWithAccess"> <div className="mx_JoinRuleSettings_spacesWithAccess">
<h4>{_t("Spaces with access")}</h4> <h4>{_t("room_settings|security|join_rule_restricted_description_spaces")}</h4>
{shownSpaces.map((room) => { {shownSpaces.map((room) => {
return ( return (
<span key={room.roomId}> <span key={room.roomId}>
@ -288,21 +292,21 @@ const JoinRuleSettings: React.FC<JoinRuleSettingsProps> = ({
); );
} else if (SpaceStore.instance.activeSpaceRoom) { } else if (SpaceStore.instance.activeSpaceRoom) {
description = _t( description = _t(
"Anyone in <spaceName/> can find and join. You can select other spaces too.", "room_settings|security|join_rule_restricted_description_active_space",
{}, {},
{ {
spaceName: () => <b>{SpaceStore.instance.activeSpaceRoom!.name}</b>, spaceName: () => <b>{SpaceStore.instance.activeSpaceRoom!.name}</b>,
}, },
); );
} else { } else {
description = _t("Anyone in a space can find and join. You can select multiple spaces."); description = _t("room_settings|security|join_rule_restricted_description_prompt");
} }
definitions.splice(1, 0, { definitions.splice(1, 0, {
value: JoinRule.Restricted, value: JoinRule.Restricted,
label: ( label: (
<> <>
{_t("Space members")} {_t("room_settings|security|join_rule_restricted")}
{preferredRestrictionVersion && upgradeRequiredPill} {preferredRestrictionVersion && upgradeRequiredPill}
</> </>
), ),
@ -317,20 +321,20 @@ const JoinRuleSettings: React.FC<JoinRuleSettingsProps> = ({
value: JoinRule.Knock, value: JoinRule.Knock,
label: ( label: (
<> <>
{_t("Ask to join")} {_t("room_settings|security|join_rule_knock")}
{preferredKnockVersion && upgradeRequiredPill} {preferredKnockVersion && upgradeRequiredPill}
</> </>
), ),
description: ( description: (
<> <>
{_t("People cannot join unless access is granted.")} {_t("room_settings|security|join_rule_knock_description")}
<LabelledCheckbox <LabelledCheckbox
className="mx_JoinRuleSettings_labelledCheckbox" className="mx_JoinRuleSettings_labelledCheckbox"
disabled={joinRule !== JoinRule.Knock} disabled={joinRule !== JoinRule.Knock}
label={ label={
room.isSpaceRoom() room.isSpaceRoom()
? _t("Make this space visible in the public room directory.") ? _t("room_settings|security|publish_space")
: _t("Make this room visible in the public room directory.") : _t("room_settings|security|publish_room")
} }
onChange={onIsPublicKnockRoomChange} onChange={onIsPublicKnockRoomChange}
value={isPublicKnockRoom} value={isPublicKnockRoom}
@ -359,21 +363,13 @@ const JoinRuleSettings: React.FC<JoinRuleSettingsProps> = ({
(roomId) => !cli.getRoom(roomId)?.currentState.maySendStateEvent(EventType.SpaceChild, userId), (roomId) => !cli.getRoom(roomId)?.currentState.maySendStateEvent(EventType.SpaceChild, userId),
); );
if (unableToUpdateSomeParents) { if (unableToUpdateSomeParents) {
warning = ( warning = <b>{_t("room_settings|security|join_rule_restricted_upgrade_warning")}</b>;
<b>
{_t(
"This room is in some spaces you're not an admin of. In those spaces, the old room will still be shown, but people will be prompted to join the new one.",
)}
</b>
);
} }
upgradeRequiredDialog( upgradeRequiredDialog(
targetVersion, targetVersion,
<> <>
{_t( {_t("room_settings|security|join_rule_restricted_upgrade_description")}
"This upgrade will allow members of selected spaces access to this room without an invite.",
)}
{warning} {warning}
</>, </>,
); );

View file

@ -749,7 +749,7 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
className="mx_UserNotifSettings_clearNotifsButton" className="mx_UserNotifSettings_clearNotifsButton"
data-testid="clear-notifications" data-testid="clear-notifications"
> >
{_t("Mark all as read")} {_t("notifications|mark_all_read")}
</AccessibleButton> </AccessibleButton>
); );
} }
@ -759,7 +759,7 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
if (clearNotifsButton) { if (clearNotifsButton) {
return ( return (
<div className="mx_UserNotifSettings_floatingSection"> <div className="mx_UserNotifSettings_floatingSection">
<div>{_t("Other")}</div> <div>{_t("notifications|class_other")}</div>
{clearNotifsButton} {clearNotifsButton}
</div> </div>
); );
@ -776,8 +776,8 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
onAdd={this.onKeywordAdd} onAdd={this.onKeywordAdd}
onRemove={this.onKeywordRemove} onRemove={this.onKeywordRemove}
disabled={this.state.phase === Phase.Persisting} disabled={this.state.phase === Phase.Persisting}
label={_t("Keyword")} label={_t("notifications|keyword")}
placeholder={_t("New keyword")} placeholder={_t("notifications|keyword_new")}
/> />
); );
} }
@ -811,11 +811,7 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
{makeRadio(r, VectorState.Loud)} {makeRadio(r, VectorState.Loud)}
{this.state.ruleIdsWithError[r.ruleId] && ( {this.state.ruleIdsWithError[r.ruleId] && (
<div className="mx_UserNotifSettings_gridRowError"> <div className="mx_UserNotifSettings_gridRowError">
<Caption isError> <Caption isError>{_t("settings|notifications|error_updating")}</Caption>
{_t(
"An error occurred when updating your notification preferences. Please try to toggle your option again.",
)}
</Caption>
</div> </div>
)} )}
</fieldset> </fieldset>
@ -824,13 +820,13 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
let sectionName: string; let sectionName: string;
switch (category) { switch (category) {
case RuleClass.VectorGlobal: case RuleClass.VectorGlobal:
sectionName = _t("Global"); sectionName = _t("notifications|class_global");
break; break;
case RuleClass.VectorMentions: case RuleClass.VectorMentions:
sectionName = _t("Mentions & keywords"); sectionName = _t("notifications|mentions_keywords");
break; break;
case RuleClass.VectorOther: case RuleClass.VectorOther:
sectionName = _t("Other"); sectionName = _t("notifications|class_other");
break; break;
default: default:
throw new Error("Developer error: Unnamed notifications section: " + category); throw new Error("Developer error: Unnamed notifications section: " + category);
@ -865,7 +861,7 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
return ( return (
<div className="mx_UserNotifSettings_floatingSection"> <div className="mx_UserNotifSettings_floatingSection">
<div>{_t("Notification targets")}</div> <div>{_t("settings|notifications|push_targets")}</div>
<table> <table>
<tbody>{rows}</tbody> <tbody>{rows}</tbody>
</table> </table>
@ -878,7 +874,7 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
// Ends up default centered // Ends up default centered
return <Spinner />; return <Spinner />;
} else if (this.state.phase === Phase.Error) { } else if (this.state.phase === Phase.Error) {
return <p data-testid="error-message">{_t("There was an error loading your notification settings.")}</p>; return <p data-testid="error-message">{_t("settings|notifications|error_loading")}</p>;
} }
return ( return (

View file

@ -123,8 +123,8 @@ export default class ProfileSettings extends React.Component<{}, IState> {
} catch (err) { } catch (err) {
logger.log("Failed to save profile", err); logger.log("Failed to save profile", err);
Modal.createDialog(ErrorDialog, { Modal.createDialog(ErrorDialog, {
title: _t("Failed to save your profile"), title: _t("settings|general|error_saving_profile_title"),
description: err instanceof Error ? err.message : _t("The operation could not be completed"), description: err instanceof Error ? err.message : _t("settings|general|error_saving_profile"),
}); });
} }
@ -184,9 +184,9 @@ export default class ProfileSettings extends React.Component<{}, IState> {
/> />
<div className="mx_ProfileSettings_profile"> <div className="mx_ProfileSettings_profile">
<div className="mx_ProfileSettings_profile_controls"> <div className="mx_ProfileSettings_profile_controls">
<SettingsSubsectionHeading heading={_t("Profile")} /> <SettingsSubsectionHeading heading={_t("common|profile")} />
<Field <Field
label={_t("Display Name")} label={_t("common|display_name")}
type="text" type="text"
value={this.state.displayName} value={this.state.displayName}
autoComplete="off" autoComplete="off"
@ -201,7 +201,7 @@ export default class ProfileSettings extends React.Component<{}, IState> {
<AvatarSetting <AvatarSetting
avatarUrl={avatarUrl} avatarUrl={avatarUrl}
avatarName={this.state.displayName || this.userId} avatarName={this.state.displayName || this.userId}
avatarAltText={_t("Profile picture")} avatarAltText={_t("common|user_avatar")}
uploadAvatar={this.uploadAvatar} uploadAvatar={this.uploadAvatar}
removeAvatar={this.removeAvatar} removeAvatar={this.removeAvatar}
/> />

View file

@ -195,11 +195,9 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> {
private deleteBackup = (): void => { private deleteBackup = (): void => {
Modal.createDialog(QuestionDialog, { Modal.createDialog(QuestionDialog, {
title: _t("Delete Backup"), title: _t("settings|security|delete_backup"),
description: _t( description: _t("settings|security|delete_backup_confirm_description"),
"Are you sure? You will lose your encrypted messages if your keys are not backed up properly.", button: _t("settings|security|delete_backup"),
),
button: _t("Delete Backup"),
danger: true, danger: true,
onFinished: (proceed) => { onFinished: (proceed) => {
if (!proceed) return; if (!proceed) return;
@ -253,36 +251,30 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> {
if (error) { if (error) {
statusDescription = ( statusDescription = (
<SettingsSubsectionText className="error"> <SettingsSubsectionText className="error">
{_t("Unable to load key backup status")} {_t("settings|security|error_loading_key_backup_status")}
</SettingsSubsectionText> </SettingsSubsectionText>
); );
} else if (loading) { } else if (loading) {
statusDescription = <Spinner />; statusDescription = <Spinner />;
} else if (backupInfo) { } else if (backupInfo) {
let restoreButtonCaption = _t("Restore from Backup"); let restoreButtonCaption = _t("settings|security|restore_key_backup");
if (this.state.activeBackupVersion !== null) { if (this.state.activeBackupVersion !== null) {
statusDescription = ( statusDescription = (
<SettingsSubsectionText> {_t("This session is backing up your keys.")}</SettingsSubsectionText> <SettingsSubsectionText> {_t("settings|security|key_backup_active")}</SettingsSubsectionText>
); );
} else { } else {
statusDescription = ( statusDescription = (
<> <>
<SettingsSubsectionText> <SettingsSubsectionText>
{_t( {_t("settings|security|key_backup_inactive", {}, { b: (sub) => <b>{sub}</b> })}
"This session is <b>not backing up your keys</b>, but you do have an existing backup you can restore from and add to going forward.",
{},
{ b: (sub) => <b>{sub}</b> },
)}
</SettingsSubsectionText> </SettingsSubsectionText>
<SettingsSubsectionText> <SettingsSubsectionText>
{_t( {_t("settings|security|key_backup_connect_prompt")}
"Connect this session to key backup before signing out to avoid losing any keys that may only be on this session.",
)}
</SettingsSubsectionText> </SettingsSubsectionText>
</> </>
); );
restoreButtonCaption = _t("Connect this session to Key Backup"); restoreButtonCaption = _t("settings|security|key_backup_connect");
} }
let uploadStatus: ReactNode; let uploadStatus: ReactNode;
@ -292,32 +284,33 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> {
} else if (sessionsRemaining > 0) { } else if (sessionsRemaining > 0) {
uploadStatus = ( uploadStatus = (
<div> <div>
{_t("Backing up %(sessionsRemaining)s keys…", { sessionsRemaining })} <br /> {_t("settings|security|key_backup_in_progress", { sessionsRemaining })} <br />
</div> </div>
); );
} else { } else {
uploadStatus = ( uploadStatus = (
<div> <div>
{_t("All keys backed up")} <br /> {_t("settings|security|key_backup_complete")} <br />
</div> </div>
); );
} }
let trustedLocally: string | undefined; let trustedLocally: string | undefined;
if (backupTrustInfo?.matchesDecryptionKey) { if (backupTrustInfo?.matchesDecryptionKey) {
trustedLocally = _t("This backup can be restored on this session"); trustedLocally = _t("settings|security|key_backup_can_be_restored");
} }
extraDetailsTableRows = ( extraDetailsTableRows = (
<> <>
<tr> <tr>
<th scope="row">{_t("Latest backup version on server:")}</th> <th scope="row">{_t("settings|security|key_backup_latest_version")}</th>
<td> <td>
{backupInfo.version} ({_t("Algorithm:")} <code>{backupInfo.algorithm}</code>) {backupInfo.version} ({_t("settings|security|key_backup_algorithm")}{" "}
<code>{backupInfo.algorithm}</code>)
</td> </td>
</tr> </tr>
<tr> <tr>
<th scope="row">{_t("Active backup version:")}</th> <th scope="row">{_t("settings|security|key_backup_active_version")}</th>
<td>{this.state.activeBackupVersion === null ? _t("None") : this.state.activeBackupVersion}</td> <td>{this.state.activeBackupVersion === null ? _t("None") : this.state.activeBackupVersion}</td>
</tr> </tr>
</> </>
@ -339,7 +332,7 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> {
if (!isSecureBackupRequired(MatrixClientPeg.safeGet())) { if (!isSecureBackupRequired(MatrixClientPeg.safeGet())) {
actions.push( actions.push(
<AccessibleButton key="delete" kind="danger" onClick={this.deleteBackup}> <AccessibleButton key="delete" kind="danger" onClick={this.deleteBackup}>
{_t("Delete Backup")} {_t("settings|security|delete_backup")}
</AccessibleButton>, </AccessibleButton>,
); );
} }
@ -347,11 +340,7 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> {
statusDescription = ( statusDescription = (
<> <>
<SettingsSubsectionText> <SettingsSubsectionText>
{_t( {_t("settings|security|key_backup_inactive_warning", {}, { b: (sub) => <b>{sub}</b> })}
"Your keys are <b>not being backed up from this session</b>.",
{},
{ b: (sub) => <b>{sub}</b> },
)}
</SettingsSubsectionText> </SettingsSubsectionText>
<SettingsSubsectionText> <SettingsSubsectionText>
{_t("Back up your keys before signing out to avoid losing them.")} {_t("Back up your keys before signing out to avoid losing them.")}
@ -377,9 +366,9 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> {
if (backupKeyCached) { if (backupKeyCached) {
backupKeyWellFormedText = ", "; backupKeyWellFormedText = ", ";
if (backupKeyWellFormed) { if (backupKeyWellFormed) {
backupKeyWellFormedText += _t("well formed"); backupKeyWellFormedText += _t("settings|security|backup_key_well_formed");
} else { } else {
backupKeyWellFormedText += _t("unexpected type"); backupKeyWellFormedText += _t("settings|security|backup_key_unexpected_type");
} }
} }
@ -390,25 +379,21 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> {
return ( return (
<> <>
<SettingsSubsectionText> <SettingsSubsectionText>{_t("settings|security|backup_keys_description")}</SettingsSubsectionText>
{_t(
"Back up your encryption keys with your account data in case you lose access to your sessions. Your keys will be secured with a unique Security Key.",
)}
</SettingsSubsectionText>
{statusDescription} {statusDescription}
<details> <details>
<summary>{_t("Advanced")}</summary> <summary>{_t("common|advanced")}</summary>
<table className="mx_SecureBackupPanel_statusList"> <table className="mx_SecureBackupPanel_statusList">
<tr> <tr>
<th scope="row">{_t("Backup key stored:")}</th> <th scope="row">{_t("settings|security|backup_key_stored_status")}</th>
<td> <td>
{backupKeyStored === true {backupKeyStored === true
? _t("settings|security|cross_signing_in_4s") ? _t("settings|security|cross_signing_in_4s")
: _t("not stored")} : _t("settings|security|cross_signing_not_stored")}
</td> </td>
</tr> </tr>
<tr> <tr>
<th scope="row">{_t("Backup key cached:")}</th> <th scope="row">{_t("settings|security|backup_key_cached_status")}</th>
<td> <td>
{backupKeyCached {backupKeyCached
? _t("settings|security|cross_signing_cached") ? _t("settings|security|cross_signing_cached")
@ -417,16 +402,20 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> {
</td> </td>
</tr> </tr>
<tr> <tr>
<th scope="row">{_t("Secret storage public key:")}</th> <th scope="row">{_t("settings|security|4s_public_key_status")}</th>
<td> <td>
{secretStorageKeyInAccount {secretStorageKeyInAccount
? _t("in account data") ? _t("settings|security|4s_public_key_in_account_data")
: _t("settings|security|cross_signing_not_found")} : _t("settings|security|cross_signing_not_found")}
</td> </td>
</tr> </tr>
<tr> <tr>
<th scope="row">{_t("Secret storage:")}</th> <th scope="row">{_t("settings|security|secret_storage_status")}</th>
<td>{secretStorageReady ? _t("ready") : _t("not ready")}</td> <td>
{secretStorageReady
? _t("settings|security|secret_storage_ready")
: _t("settings|security|secret_storage_not_ready")}
</td>
</tr> </tr>
{extraDetailsTableRows} {extraDetailsTableRows}
</table> </table>

View file

@ -55,7 +55,7 @@ const FilteredDeviceListHeader: React.FC<Props> = ({
<span className="mx_FilteredDeviceListHeader_label"> <span className="mx_FilteredDeviceListHeader_label">
{selectedDeviceCount > 0 {selectedDeviceCount > 0
? _t("settings|sessions|n_sessions_selected", { count: selectedDeviceCount }) ? _t("settings|sessions|n_sessions_selected", { count: selectedDeviceCount })
: _t("Sessions")} : _t("settings|sessions|title")}
</span> </span>
{children} {children}
</div> </div>

View file

@ -118,7 +118,7 @@ export function NotificationPusherSettings(): JSX.Element {
</SettingsIndent> </SettingsIndent>
</SettingsSubsection> </SettingsSubsection>
{notificationTargets.length > 0 && ( {notificationTargets.length > 0 && (
<SettingsSubsection heading={_t("Notification targets")}> <SettingsSubsection heading={_t("settings|notifications|push_targets")}>
<ul> <ul>
{pushers {pushers
.filter((it) => it.kind !== "email") .filter((it) => it.kind !== "email")

View file

@ -119,7 +119,7 @@ export default function NotificationSettings2(): JSX.Element {
)} )}
</SettingsBanner> </SettingsBanner>
)} )}
<SettingsSection heading={_t("Notifications")}> <SettingsSection heading={_t("notifications|enable_prompt_toast_title")}>
<div className="mx_SettingsSubsection_content mx_NotificationSettings2_flags"> <div className="mx_SettingsSubsection_content mx_NotificationSettings2_flags">
<LabelledToggleSwitch <LabelledToggleSwitch
label={_t("settings|notifications|enable_notifications_account")} label={_t("settings|notifications|enable_notifications_account")}
@ -343,8 +343,8 @@ export default function NotificationSettings2(): JSX.Element {
keywords: model!.keywords.filter((it) => it !== keyword), keywords: model!.keywords.filter((it) => it !== keyword),
}); });
}} }}
label={_t("Keyword")} label={_t("notifications|keyword")}
placeholder={_t("New keyword")} placeholder={_t("notifications|keyword_new")}
/> />
</SettingsSubsection> </SettingsSubsection>
<NotificationPusherSettings /> <NotificationPusherSettings />

View file

@ -155,7 +155,7 @@ export default class AdvancedRoomSettingsTab extends React.Component<IProps, ISt
return ( return (
<SettingsTab> <SettingsTab>
<SettingsSection heading={_t("Advanced")}> <SettingsSection heading={_t("common|advanced")}>
<SettingsSubsection heading={room.isSpaceRoom() ? _t("Space information") : _t("Room information")}> <SettingsSubsection heading={room.isSpaceRoom() ? _t("Space information") : _t("Room information")}>
<div> <div>
<span>{_t("room_settings|advanced|room_id")}</span> <span>{_t("room_settings|advanced|room_id")}</span>

View file

@ -85,7 +85,7 @@ export default class GeneralRoomSettingsTab extends React.Component<IProps, ISta
return ( return (
<SettingsTab data-testid="General"> <SettingsTab data-testid="General">
<SettingsSection heading={_t("General")}> <SettingsSection heading={_t("common|general")}>
<RoomProfileSettings roomId={room.roomId} /> <RoomProfileSettings roomId={room.roomId} />
</SettingsSection> </SettingsSection>

View file

@ -171,7 +171,7 @@ export default class NotificationsSettingsTab extends React.Component<IProps, IS
return ( return (
<SettingsTab> <SettingsTab>
<SettingsSection heading={_t("Notifications")}> <SettingsSection heading={_t("notifications|enable_prompt_toast_title")}>
<div className="mx_NotificationSettingsTab_notificationsSection"> <div className="mx_NotificationSettingsTab_notificationsSection">
<StyledRadioGroup <StyledRadioGroup
name="roomNotificationSetting" name="roomNotificationSetting"

View file

@ -277,7 +277,7 @@ export default class SecurityRoomSettingsTab extends React.Component<IProps, ISt
className="mx_SettingsTab_showAdvanced" className="mx_SettingsTab_showAdvanced"
aria-expanded={this.state.showAdvancedSection} aria-expanded={this.state.showAdvancedSection}
> >
{this.state.showAdvancedSection ? _t("Hide advanced") : _t("Show advanced")} {this.state.showAdvancedSection ? _t("action|hide_advanced") : _t("action|show_advanced")}
</AccessibleButton> </AccessibleButton>
{this.state.showAdvancedSection && this.renderAdvanced()} {this.state.showAdvancedSection && this.renderAdvanced()}
</div> </div>
@ -285,7 +285,7 @@ export default class SecurityRoomSettingsTab extends React.Component<IProps, ISt
} }
return ( return (
<SettingsFieldset legend={_t("Access")} description={description}> <SettingsFieldset legend={_t("room_settings|access|title")} description={description}>
<JoinRuleSettings <JoinRuleSettings
room={room} room={room}
beforeChange={this.onBeforeJoinRuleChange} beforeChange={this.onBeforeJoinRuleChange}
@ -411,7 +411,7 @@ export default class SecurityRoomSettingsTab extends React.Component<IProps, ISt
value={guestAccess === GuestAccess.CanJoin} value={guestAccess === GuestAccess.CanJoin}
onChange={this.onGuestAccessChange} onChange={this.onGuestAccessChange}
disabled={!canSetGuestAccess} disabled={!canSetGuestAccess}
label={_t("Enable guest access")} label={_t("room_settings|visibility|guest_access_label")}
/> />
<p>{_t("room_settings|security|guest_access_warning")}</p> <p>{_t("room_settings|security|guest_access_warning")}</p>
</div> </div>

View file

@ -99,7 +99,7 @@ export default class AppearanceUserSettingsTab extends React.Component<IProps, I
onClick={() => this.setState({ showAdvanced: !this.state.showAdvanced })} onClick={() => this.setState({ showAdvanced: !this.state.showAdvanced })}
aria-expanded={this.state.showAdvanced} aria-expanded={this.state.showAdvanced}
> >
{this.state.showAdvanced ? _t("Hide advanced") : _t("Show advanced")} {this.state.showAdvanced ? _t("action|hide_advanced") : _t("action|show_advanced")}
</AccessibleButton> </AccessibleButton>
); );

View file

@ -561,7 +561,7 @@ export default class GeneralUserSettingsTab extends React.Component<IProps, ISta
return ( return (
<SettingsTab data-testid="mx_GeneralUserSettingsTab"> <SettingsTab data-testid="mx_GeneralUserSettingsTab">
<SettingsSection heading={_t("General")}> <SettingsSection heading={_t("common|general")}>
<ProfileSettings /> <ProfileSettings />
{this.renderAccountSection()} {this.renderAccountSection()}
{this.renderLanguageSection()} {this.renderLanguageSection()}

View file

@ -322,7 +322,7 @@ export default class HelpUserSettingsTab extends React.Component<IProps, IState>
</SettingsSubsection> </SettingsSubsection>
{this.renderLegal()} {this.renderLegal()}
{this.renderCredits()} {this.renderCredits()}
<SettingsSubsection heading={_t("Advanced")}> <SettingsSubsection heading={_t("common|advanced")}>
<SettingsSubsectionText> <SettingsSubsectionText>
{_t( {_t(
"setting|help_about|homeserver", "setting|help_about|homeserver",

View file

@ -33,7 +33,7 @@ export default class NotificationUserSettingsTab extends React.Component {
{newNotificationSettingsEnabled ? ( {newNotificationSettingsEnabled ? (
<NotificationSettings2 /> <NotificationSettings2 />
) : ( ) : (
<SettingsSection heading={_t("Notifications")}> <SettingsSection heading={_t("notifications|enable_prompt_toast_title")}>
<Notifications /> <Notifications />
</SettingsSection> </SettingsSection>
)} )}

View file

@ -152,7 +152,7 @@ export default class PreferencesUserSettingsTab extends React.Component<IProps,
</SettingsSubsection> </SettingsSubsection>
)} )}
<SettingsSubsection heading={_t("Spaces")}> <SettingsSubsection heading={_t("common|spaces")}>
{this.renderGroup(PreferencesUserSettingsTab.SPACES_SETTINGS, SettingLevel.ACCOUNT)} {this.renderGroup(PreferencesUserSettingsTab.SPACES_SETTINGS, SettingLevel.ACCOUNT)}
</SettingsSubsection> </SettingsSubsection>
@ -204,7 +204,7 @@ export default class PreferencesUserSettingsTab extends React.Component<IProps,
{this.renderGroup(PreferencesUserSettingsTab.ROOM_DIRECTORY_SETTINGS)} {this.renderGroup(PreferencesUserSettingsTab.ROOM_DIRECTORY_SETTINGS)}
</SettingsSubsection> </SettingsSubsection>
<SettingsSubsection heading={_t("General")} stretchContent> <SettingsSubsection heading={_t("common|general")} stretchContent>
{this.renderGroup(PreferencesUserSettingsTab.GENERAL_SETTINGS)} {this.renderGroup(PreferencesUserSettingsTab.GENERAL_SETTINGS)}
<SettingsFlag name="Electron.showTrayIcon" level={SettingLevel.PLATFORM} hideIfCannotSet /> <SettingsFlag name="Electron.showTrayIcon" level={SettingLevel.PLATFORM} hideIfCannotSet />

View file

@ -252,21 +252,21 @@ export default class SecurityUserSettingsTab extends React.Component<IProps, ISt
} }
return ( return (
<SettingsSubsection heading={_t("Bulk options")}> <SettingsSubsection heading={_t("settings|security|bulk_options_section")}>
<div className="mx_SecurityUserSettingsTab_bulkOptions"> <div className="mx_SecurityUserSettingsTab_bulkOptions">
<AccessibleButton <AccessibleButton
onClick={this.onAcceptAllInvitesClicked} onClick={this.onAcceptAllInvitesClicked}
kind="primary" kind="primary"
disabled={this.state.managingInvites} disabled={this.state.managingInvites}
> >
{_t("Accept all %(invitedRooms)s invites", { invitedRooms: invitedRoomIds.size })} {_t("settings|security|bulk_options_accept_all_invites", { invitedRooms: invitedRoomIds.size })}
</AccessibleButton> </AccessibleButton>
<AccessibleButton <AccessibleButton
onClick={this.onRejectAllInvitesClicked} onClick={this.onRejectAllInvitesClicked}
kind="danger" kind="danger"
disabled={this.state.managingInvites} disabled={this.state.managingInvites}
> >
{_t("Reject all %(invitedRooms)s invites", { invitedRooms: invitedRoomIds.size })} {_t("settings|security|bulk_options_reject_all_invites", { invitedRooms: invitedRoomIds.size })}
</AccessibleButton> </AccessibleButton>
{this.state.managingInvites ? <InlineSpinner /> : <div />} {this.state.managingInvites ? <InlineSpinner /> : <div />}
</div> </div>
@ -282,7 +282,7 @@ export default class SecurityUserSettingsTab extends React.Component<IProps, ISt
); );
const eventIndex = ( const eventIndex = (
<SettingsSubsection heading={_t("Message search")}> <SettingsSubsection heading={_t("settings|security|message_search_section")}>
<EventIndexPanel /> <EventIndexPanel />
</SettingsSubsection> </SettingsSubsection>
); );
@ -331,7 +331,7 @@ export default class SecurityUserSettingsTab extends React.Component<IProps, ISt
<SettingsFlag name="pseudonymousAnalyticsOptIn" level={SettingLevel.ACCOUNT} /> <SettingsFlag name="pseudonymousAnalyticsOptIn" level={SettingLevel.ACCOUNT} />
)} )}
</SettingsSubsection> </SettingsSubsection>
<SettingsSubsection heading={_t("Sessions")}> <SettingsSubsection heading={_t("settings|sessions|title")}>
<SettingsFlag name="deviceClientInformationOptIn" level={SettingLevel.ACCOUNT} /> <SettingsFlag name="deviceClientInformationOptIn" level={SettingLevel.ACCOUNT} />
</SettingsSubsection> </SettingsSubsection>
</SettingsSection> </SettingsSection>
@ -346,7 +346,7 @@ export default class SecurityUserSettingsTab extends React.Component<IProps, ISt
// only show the section if there's something to show // only show the section if there's something to show
if (ignoreUsersPanel || invitesPanel || e2ePanel) { if (ignoreUsersPanel || invitesPanel || e2ePanel) {
advancedSection = ( advancedSection = (
<SettingsSection heading={_t("Advanced")}> <SettingsSection heading={_t("common|advanced")}>
{ignoreUsersPanel} {ignoreUsersPanel}
{invitesPanel} {invitesPanel}
{e2ePanel} {e2ePanel}

View file

@ -48,7 +48,7 @@ const confirmSignOut = async (sessionsToSignOutCount: number): Promise<boolean>
description: ( description: (
<div> <div>
<p> <p>
{_t("Are you sure you want to sign out of %(count)s sessions?", { {_t("settings|sessions|sign_out_confirm_description", {
count: sessionsToSignOutCount, count: sessionsToSignOutCount,
})} })}
</p> </p>
@ -275,7 +275,7 @@ const SessionManagerTab: React.FC = () => {
return ( return (
<SettingsTab> <SettingsTab>
<SettingsSection heading={_t("Sessions")}> <SettingsSection heading={_t("settings|sessions|title")}>
<SecurityRecommendations <SecurityRecommendations
devices={devices} devices={devices}
goToFilteredList={onGoToFilteredList} goToFilteredList={onGoToFilteredList}

View file

@ -64,9 +64,9 @@ const SidebarUserSettingsTab: React.FC = () => {
return ( return (
<SettingsTab> <SettingsTab>
<SettingsSection heading={_t("Sidebar")}> <SettingsSection heading={_t("settings|sidebar|title")}>
<SettingsSubsection <SettingsSubsection
heading={_t("Spaces to show")} heading={_t("settings|sidebar|metaspaces_subsection")}
description={_t( description={_t(
"Spaces are ways to group rooms and people. Alongside the spaces you're in, you can use some pre-built ones too.", "Spaces are ways to group rooms and people. Alongside the spaces you're in, you can use some pre-built ones too.",
)} )}
@ -82,7 +82,7 @@ const SidebarUserSettingsTab: React.FC = () => {
{_t("common|home")} {_t("common|home")}
</SettingsSubsectionText> </SettingsSubsectionText>
<SettingsSubsectionText> <SettingsSubsectionText>
{_t("Home is useful for getting an overview of everything.")} {_t("settings|sidebar|metaspaces_home_description")}
</SettingsSubsectionText> </SettingsSubsectionText>
</StyledCheckbox> </StyledCheckbox>
@ -93,9 +93,11 @@ const SidebarUserSettingsTab: React.FC = () => {
className="mx_SidebarUserSettingsTab_checkbox mx_SidebarUserSettingsTab_homeAllRoomsCheckbox" className="mx_SidebarUserSettingsTab_checkbox mx_SidebarUserSettingsTab_homeAllRoomsCheckbox"
data-testid="mx_SidebarUserSettingsTab_homeAllRoomsCheckbox" data-testid="mx_SidebarUserSettingsTab_homeAllRoomsCheckbox"
> >
<SettingsSubsectionText>{_t("Show all rooms")}</SettingsSubsectionText>
<SettingsSubsectionText> <SettingsSubsectionText>
{_t("Show all your rooms in Home, even if they're in a space.")} {_t("settings|sidebar|metaspaces_home_all_rooms")}
</SettingsSubsectionText>
<SettingsSubsectionText>
{_t("settings|sidebar|metaspaces_home_all_rooms_description")}
</SettingsSubsectionText> </SettingsSubsectionText>
</StyledCheckbox> </StyledCheckbox>
@ -109,7 +111,7 @@ const SidebarUserSettingsTab: React.FC = () => {
{_t("common|favourites")} {_t("common|favourites")}
</SettingsSubsectionText> </SettingsSubsectionText>
<SettingsSubsectionText> <SettingsSubsectionText>
{_t("Group all your favourite rooms and people in one place.")} {_t("settings|sidebar|metaspaces_favourites_description")}
</SettingsSubsectionText> </SettingsSubsectionText>
</StyledCheckbox> </StyledCheckbox>
@ -122,7 +124,9 @@ const SidebarUserSettingsTab: React.FC = () => {
<MembersIcon /> <MembersIcon />
{_t("common|people")} {_t("common|people")}
</SettingsSubsectionText> </SettingsSubsectionText>
<SettingsSubsectionText>{_t("Group all your people in one place.")}</SettingsSubsectionText> <SettingsSubsectionText>
{_t("settings|sidebar|metaspaces_people_description")}
</SettingsSubsectionText>
</StyledCheckbox> </StyledCheckbox>
<StyledCheckbox <StyledCheckbox
@ -132,10 +136,10 @@ const SidebarUserSettingsTab: React.FC = () => {
> >
<SettingsSubsectionText> <SettingsSubsectionText>
<HashCircleIcon /> <HashCircleIcon />
{_t("Rooms outside of a space")} {_t("settings|sidebar|metaspaces_orphans")}
</SettingsSubsectionText> </SettingsSubsectionText>
<SettingsSubsectionText> <SettingsSubsectionText>
{_t("Group all your rooms that aren't part of a space in one place.")} {_t("settings|sidebar|metaspaces_orphans_description")}
</SettingsSubsectionText> </SettingsSubsectionText>
</StyledCheckbox> </StyledCheckbox>
</SettingsSubsection> </SettingsSubsection>

View file

@ -200,7 +200,7 @@ export default class VoiceUserSettingsTab extends React.Component<{}, IState> {
</SettingsSubsection> </SettingsSubsection>
</SettingsSection> </SettingsSection>
<SettingsSection heading={_t("Advanced")}> <SettingsSection heading={_t("common|advanced")}>
<SettingsSubsection heading={_t("Voice processing")}> <SettingsSubsection heading={_t("Voice processing")}>
<LabelledToggleSwitch <LabelledToggleSwitch
value={this.state.audioNoiseSuppression} value={this.state.audioNoiseSuppression}

View file

@ -58,7 +58,7 @@ const QuickSettingsButton: React.FC<{
managed={false} managed={false}
focusLock={true} focusLock={true}
> >
<h2>{_t("Quick settings")}</h2> <h2>{_t("quick_settings|title")}</h2>
<AccessibleButton <AccessibleButton
onClick={() => { onClick={() => {
@ -67,7 +67,7 @@ const QuickSettingsButton: React.FC<{
}} }}
kind="primary_outline" kind="primary_outline"
> >
{_t("All settings")} {_t("quick_settings|all_settings")}
</AccessibleButton> </AccessibleButton>
{currentRoomId && developerModeEnabled && ( {currentRoomId && developerModeEnabled && (
@ -90,7 +90,7 @@ const QuickSettingsButton: React.FC<{
<h4 className="mx_QuickSettingsButton_pinToSidebarHeading"> <h4 className="mx_QuickSettingsButton_pinToSidebarHeading">
<PinUprightIcon className="mx_QuickSettingsButton_icon" /> <PinUprightIcon className="mx_QuickSettingsButton_icon" />
{_t("Pin to sidebar")} {_t("quick_settings|metaspace_section")}
</h4> </h4>
<StyledCheckbox <StyledCheckbox
@ -120,7 +120,7 @@ const QuickSettingsButton: React.FC<{
}} }}
> >
<EllipsisIcon className="mx_QuickSettingsButton_icon" /> <EllipsisIcon className="mx_QuickSettingsButton_icon" />
{_t("More options")} {_t("quick_settings|sidebar_settings")}
</AccessibleButton> </AccessibleButton>
<QuickThemeSwitcher requestClose={closeMenu} /> <QuickThemeSwitcher requestClose={closeMenu} />
@ -133,7 +133,7 @@ const QuickSettingsButton: React.FC<{
<AccessibleTooltipButton <AccessibleTooltipButton
className={classNames("mx_QuickSettingsButton", { expanded: !isPanelCollapsed })} className={classNames("mx_QuickSettingsButton", { expanded: !isPanelCollapsed })}
onClick={openMenu} onClick={openMenu}
title={_t("Quick settings")} title={_t("quick_settings|title")}
inputRef={handle} inputRef={handle}
forceHide={!isPanelCollapsed} forceHide={!isPanelCollapsed}
aria-expanded={!isPanelCollapsed} aria-expanded={!isPanelCollapsed}

View file

@ -45,7 +45,7 @@ const QuickThemeSwitcher: React.FC<Props> = ({ requestClose }) => {
const themeOptions = [ const themeOptions = [
{ {
id: MATCH_SYSTEM_THEME_ID, id: MATCH_SYSTEM_THEME_ID,
name: _t("Match system"), name: _t("theme|match_system"),
}, },
...orderedThemes, ...orderedThemes,
]; ];
@ -85,7 +85,7 @@ const QuickThemeSwitcher: React.FC<Props> = ({ requestClose }) => {
id="mx_QuickSettingsButton_themePickerDropdown" id="mx_QuickSettingsButton_themePickerDropdown"
onOptionChange={onOptionChange} onOptionChange={onOptionChange}
value={selectedTheme} value={selectedTheme}
label={_t("Space selection")} label={_t("common|theme")}
> >
{ {
themeOptions.map((theme) => <div key={theme.id}>{theme.name}</div>) as NonEmptyArray< themeOptions.map((theme) => <div key={theme.id}>{theme.name}</div>) as NonEmptyArray<

View file

@ -67,7 +67,7 @@ export const SpaceAvatar: React.FC<Pick<IProps, "avatarUrl" | "avatarDisabled" |
}} }}
kind="link" kind="link"
className="mx_SpaceBasicSettings_avatar_remove" className="mx_SpaceBasicSettings_avatar_remove"
aria-label={_t("Delete avatar")} aria-label={_t("room_settings|delete_avatar_label")}
> >
{_t("action|delete")} {_t("action|delete")}
</AccessibleButton> </AccessibleButton>
@ -84,7 +84,7 @@ export const SpaceAvatar: React.FC<Pick<IProps, "avatarUrl" | "avatarDisabled" |
<AccessibleButton <AccessibleButton
onClick={() => avatarUploadRef.current?.click()} onClick={() => avatarUploadRef.current?.click()}
kind="link" kind="link"
aria-label={_t("Upload avatar")} aria-label={_t("room_settings|upload_avatar_label")}
> >
{_t("action|upload")} {_t("action|upload")}
</AccessibleButton> </AccessibleButton>

View file

@ -145,7 +145,7 @@ const SpaceChildrenPicker: React.FC<IProps> = ({
{state === Target.Specific && ( {state === Target.Specific && (
<SpecificChildrenPicker <SpecificChildrenPicker
filterPlaceholder={_t("Search %(spaceName)s", { spaceName: space.name })} filterPlaceholder={_t("space|search_children", { spaceName: space.name })}
rooms={spaceChildren} rooms={spaceChildren}
selected={selected} selected={selected}
onChange={(isSelected: boolean, room: Room) => { onChange={(isSelected: boolean, room: Room) => {

View file

@ -193,8 +193,8 @@ export const SpaceCreateForm: React.FC<ISpaceCreateFormProps> = ({
onChange={setAlias} onChange={setAlias}
domain={domain} domain={domain}
value={alias} value={alias}
placeholder={name ? nameToLocalpart(name) : _t("create_space|name_placeholder")} placeholder={name ? nameToLocalpart(name) : _t("create_space|address_placeholder")}
label={_t("Address")} label={_t("create_space|address_label")}
disabled={busy} disabled={busy}
onKeyDown={onKeyDown} onKeyDown={onKeyDown}
/> />
@ -284,7 +284,7 @@ const SpaceCreateMenu: React.FC<{
if (visibility === null) { if (visibility === null) {
body = ( body = (
<React.Fragment> <React.Fragment>
<h2>{_t("Create a space")}</h2> <h2>{_t("create_space|label")}</h2>
<p>{_t("create_space|explainer")}</p> <p>{_t("create_space|explainer")}</p>
<SpaceCreateMenuType <SpaceCreateMenuType
@ -322,7 +322,7 @@ const SpaceCreateMenu: React.FC<{
: _t("create_space|private_heading")} : _t("create_space|private_heading")}
</h2> </h2>
<p> <p>
{_t("create_space|add_details_prompt")} {_t("You can change these anytime.")} {_t("create_space|add_details_prompt")} {_t("create_space|add_details_prompt_2")}
</p> </p>
<SpaceCreateForm <SpaceCreateForm
@ -341,7 +341,7 @@ const SpaceCreateMenu: React.FC<{
/> />
<AccessibleButton kind="primary" onClick={onSpaceCreateClick} disabled={busy}> <AccessibleButton kind="primary" onClick={onSpaceCreateClick} disabled={busy}>
{busy ? _t("Creating…") : _t("action|create")} {busy ? _t("create_space|creating") : _t("action|create")}
</AccessibleButton> </AccessibleButton>
</React.Fragment> </React.Fragment>
); );

View file

@ -101,7 +101,7 @@ export const HomeButtonContextMenu: React.FC<ComponentProps<typeof SpaceContextM
<IconizedContextMenuOptionList first> <IconizedContextMenuOptionList first>
<IconizedContextMenuCheckbox <IconizedContextMenuCheckbox
iconClassName="mx_SpacePanel_noIcon" iconClassName="mx_SpacePanel_noIcon"
label={_t("Show all rooms")} label={_t("settings|sidebar|metaspaces_home_all_rooms")}
active={allRoomsInHome} active={allRoomsInHome}
onClick={() => { onClick={() => {
onFinished(); onFinished();
@ -245,7 +245,7 @@ const CreateSpaceButton: React.FC<Pick<IInnerSpacePanelProps, "isPanelCollapsed"
className={classNames("mx_SpaceButton_new", { className={classNames("mx_SpaceButton_new", {
mx_SpaceButton_newCancel: menuDisplayed, mx_SpaceButton_newCancel: menuDisplayed,
})} })}
label={menuDisplayed ? _t("action|cancel") : _t("Create a space")} label={menuDisplayed ? _t("action|cancel") : _t("create_space|label")}
onClick={onNewClick} onClick={onNewClick}
isNarrow={isPanelCollapsed} isNarrow={isPanelCollapsed}
innerRef={handle} innerRef={handle}
@ -297,7 +297,7 @@ const InnerSpacePanel = React.memo<IInnerSpacePanelProps>(
} }
element="ul" element="ul"
role="tree" role="tree"
aria-label={_t("Spaces")} aria-label={_t("common|spaces")}
> >
{metaSpacesSection} {metaSpacesSection}
{invites.map((s) => ( {invites.map((s) => (

View file

@ -33,7 +33,7 @@ interface IProps {
} }
const SpacePublicShare: React.FC<IProps> = ({ space, onFinished }) => { const SpacePublicShare: React.FC<IProps> = ({ space, onFinished }) => {
const [copiedText, setCopiedText] = useState(_t("Click to copy")); const [copiedText, setCopiedText] = useState(_t("action|click_to_copy"));
return ( return (
<div className="mx_SpacePublicShare"> <div className="mx_SpacePublicShare">
@ -43,16 +43,16 @@ const SpacePublicShare: React.FC<IProps> = ({ space, onFinished }) => {
const permalinkCreator = new RoomPermalinkCreator(space); const permalinkCreator = new RoomPermalinkCreator(space);
permalinkCreator.load(); permalinkCreator.load();
const success = await copyPlaintext(permalinkCreator.forShareableRoom()); const success = await copyPlaintext(permalinkCreator.forShareableRoom());
const text = success ? _t("Copied!") : _t("Failed to copy"); const text = success ? _t("common|copied") : _t("error|failed_copy");
setCopiedText(text); setCopiedText(text);
await sleep(5000); await sleep(5000);
if (copiedText === text) { if (copiedText === text) {
// if the text hasn't changed by another click then clear it after some time // if the text hasn't changed by another click then clear it after some time
setCopiedText(_t("Click to copy")); setCopiedText(_t("action|click_to_copy"));
} }
}} }}
> >
{_t("Share invite link")} {_t("space|invite_link")}
<div>{copiedText}</div> <div>{copiedText}</div>
</AccessibleButton> </AccessibleButton>
{space.canInvite(MatrixClientPeg.safeGet().getSafeUserId()) && {space.canInvite(MatrixClientPeg.safeGet().getSafeUserId()) &&
@ -64,8 +64,8 @@ const SpacePublicShare: React.FC<IProps> = ({ space, onFinished }) => {
showRoomInviteDialog(space.roomId); showRoomInviteDialog(space.roomId);
}} }}
> >
{_t("Invite people")} {_t("space|invite")}
<div>{_t("Invite with email or username")}</div> <div>{_t("space|invite_description")}</div>
</AccessibleButton> </AccessibleButton>
) : null} ) : null}
</div> </div>

View file

@ -90,15 +90,15 @@ const SpaceSettingsGeneralTab: React.FC<IProps> = ({ matrixClient: cli, space })
const failures = results.filter((r) => r.status === "rejected"); const failures = results.filter((r) => r.status === "rejected");
if (failures.length > 0) { if (failures.length > 0) {
logger.error("Failed to save space settings: ", failures); logger.error("Failed to save space settings: ", failures);
setError(_t("Failed to save space settings.")); setError(_t("room_settings|general|error_save_space_settings"));
} }
}; };
return ( return (
<SettingsTab> <SettingsTab>
<SettingsSection heading={_t("General")}> <SettingsSection heading={_t("common|general")}>
<div> <div>
<div>{_t("Edit settings relating to your space.")}</div> <div>{_t("room_settings|general|description_space")}</div>
{error && <div className="mx_SpaceRoomView_errorText">{error}</div>} {error && <div className="mx_SpaceRoomView_errorText">{error}</div>}
@ -122,18 +122,18 @@ const SpaceSettingsGeneralTab: React.FC<IProps> = ({ matrixClient: cli, space })
{_t("action|cancel")} {_t("action|cancel")}
</AccessibleButton> </AccessibleButton>
<AccessibleButton onClick={onSave} disabled={busy} kind="primary"> <AccessibleButton onClick={onSave} disabled={busy} kind="primary">
{busy ? _t("Saving…") : _t("Save Changes")} {busy ? _t("common|saving") : _t("room_settings|general|save")}
</AccessibleButton> </AccessibleButton>
</div> </div>
<SettingsSubsection heading={_t("Leave Space")}> <SettingsSubsection heading={_t("room_settings|general|leave_space")}>
<AccessibleButton <AccessibleButton
kind="danger" kind="danger"
onClick={() => { onClick={() => {
leaveSpace(space); leaveSpace(space);
}} }}
> >
{_t("Leave Space")} {_t("room_settings|general|leave_space")}
</AccessibleButton> </AccessibleButton>
</SettingsSubsection> </SettingsSubsection>
</SettingsSection> </SettingsSection>

View file

@ -64,7 +64,7 @@ const SpaceSettingsVisibilityTab: React.FC<IProps> = ({ matrixClient: cli, space
}, },
"", "",
), ),
() => setError(_t("Failed to update the guest access of this space")), () => setError(_t("room_settings|visibility|error_update_guest_access")),
); );
const [historyVisibility, setHistoryVisibility] = useLocalEcho<HistoryVisibility>( const [historyVisibility, setHistoryVisibility] = useLocalEcho<HistoryVisibility>(
() => () =>
@ -79,7 +79,7 @@ const SpaceSettingsVisibilityTab: React.FC<IProps> = ({ matrixClient: cli, space
}, },
"", "",
), ),
() => setError(_t("Failed to update the history visibility of this space")), () => setError(_t("room_settings|visibility|error_update_history_visibility")),
); );
const [showAdvancedSection, toggleAdvancedSection] = useStateToggle(); const [showAdvancedSection, toggleAdvancedSection] = useStateToggle();
@ -100,7 +100,7 @@ const SpaceSettingsVisibilityTab: React.FC<IProps> = ({ matrixClient: cli, space
className="mx_SettingsTab_showAdvanced" className="mx_SettingsTab_showAdvanced"
aria-expanded={showAdvancedSection} aria-expanded={showAdvancedSection}
> >
{showAdvancedSection ? _t("Hide advanced") : _t("Show advanced")} {showAdvancedSection ? _t("action|hide_advanced") : _t("action|show_advanced")}
</AccessibleButton> </AccessibleButton>
{showAdvancedSection && ( {showAdvancedSection && (
@ -109,12 +109,12 @@ const SpaceSettingsVisibilityTab: React.FC<IProps> = ({ matrixClient: cli, space
value={guestAccessEnabled} value={guestAccessEnabled}
onChange={setGuestAccessEnabled} onChange={setGuestAccessEnabled}
disabled={!canSetGuestAccess} disabled={!canSetGuestAccess}
label={_t("Enable guest access")} label={_t("room_settings|visibility|guest_access_label")}
/> />
<p> <p>
{_t("Guests can join a space without having an account.")} {_t("room_settings|visibility|guest_access_explainer")}
<br /> <br />
{_t("This may be useful for public spaces.")} {_t("room_settings|visibility|guest_access_explainer_public_space")}
</p> </p>
</div> </div>
)} )}
@ -139,7 +139,7 @@ const SpaceSettingsVisibilityTab: React.FC<IProps> = ({ matrixClient: cli, space
return ( return (
<SettingsTab> <SettingsTab>
<SettingsSection heading={_t("Visibility")}> <SettingsSection heading={_t("room_settings|visibility|title")}>
{error && ( {error && (
<div data-testid="space-settings-error" className="mx_SpaceRoomView_errorText"> <div data-testid="space-settings-error" className="mx_SpaceRoomView_errorText">
{error} {error}
@ -148,12 +148,12 @@ const SpaceSettingsVisibilityTab: React.FC<IProps> = ({ matrixClient: cli, space
<SettingsFieldset <SettingsFieldset
data-testid="access-fieldset" data-testid="access-fieldset"
legend={_t("Access")} legend={_t("room_settings|access|title")}
description={_t("Decide who can view and join %(spaceName)s.", { spaceName: space.name })} description={_t("room_settings|access|description_space", { spaceName: space.name })}
> >
<JoinRuleSettings <JoinRuleSettings
room={space} room={space}
onError={(): void => setError(_t("Failed to update the visibility of this space"))} onError={(): void => setError(_t("room_settings|visibility|error_failed_save"))}
closeSettingsFn={closeSettingsFn} closeSettingsFn={closeSettingsFn}
/> />
{advancedSection} {advancedSection}
@ -166,12 +166,12 @@ const SpaceSettingsVisibilityTab: React.FC<IProps> = ({ matrixClient: cli, space
); );
}} }}
disabled={!canSetHistoryVisibility} disabled={!canSetHistoryVisibility}
label={_t("Preview Space")} label={_t("room_settings|visibility|history_visibility_anyone_space")}
/> />
<p> <p>
{_t("Allow people to preview your space before they join.")} {_t("room_settings|visibility|history_visibility_anyone_space_description")}
<br /> <br />
<b>{_t("Recommended for public spaces.")}</b> <b>{_t("room_settings|visibility|history_visibility_anyone_space_recommendation")}</b>
</p> </p>
</div> </div>
</SettingsFieldset> </SettingsFieldset>

View file

@ -95,9 +95,9 @@ export const SpaceButton: React.FC<IButtonProps> = ({
let notifBadge; let notifBadge;
if (spaceKey && notificationState) { if (spaceKey && notificationState) {
let ariaLabel = _t("Jump to first unread room."); let ariaLabel = _t("a11y_jump_first_unread_room");
if (space?.getMyMembership() === "invite") { if (space?.getMyMembership() === "invite") {
ariaLabel = _t("Jump to first invite."); ariaLabel = _t("a11y|jump_first_invite");
} }
const jumpToNotification = (ev: MouseEvent): void => { const jumpToNotification = (ev: MouseEvent): void => {
@ -371,7 +371,7 @@ export class SpaceItem extends React.PureComponent<IItemProps, IItemState> {
className={isInvite ? "mx_SpaceButton_invite" : undefined} className={isInvite ? "mx_SpaceButton_invite" : undefined}
selected={selected} selected={selected}
label={this.state.name} label={this.state.name}
contextMenuTooltip={_t("Space options")} contextMenuTooltip={_t("space|context_menu|options")}
notificationState={notificationState} notificationState={notificationState}
isNarrow={isPanelCollapsed} isNarrow={isPanelCollapsed}
size={isNested ? "24px" : "32px"} size={isNested ? "24px" : "32px"}

View file

@ -87,7 +87,7 @@ export default class InlineTermsAgreement extends React.Component<IProps, IState
for (let i = 0; i < this.state.policies.length; i++) { for (let i = 0; i < this.state.policies.length; i++) {
const policy = this.state.policies[i]; const policy = this.state.policies[i];
const introText = _t( const introText = _t(
"Accept <policyLink /> to continue:", "terms|inline_intro_text",
{}, {},
{ {
policyLink: () => { policyLink: () => {

View file

@ -31,7 +31,7 @@ export default class NonUrgentEchoFailureToast extends React.PureComponent {
<div className="mx_NonUrgentEchoFailureToast"> <div className="mx_NonUrgentEchoFailureToast">
<span className="mx_NonUrgentEchoFailureToast_icon" /> <span className="mx_NonUrgentEchoFailureToast_icon" />
{_t( {_t(
"Your server isn't responding to some <a>requests</a>.", "error|non_urgent_echo_failure_toast",
{}, {},
{ {
a: (sub) => ( a: (sub) => (

View file

@ -158,7 +158,7 @@ export default class VerificationRequestToast extends React.PureComponent<IProps
if (request.isSelfVerification) { if (request.isSelfVerification) {
if (this.state.device) { if (this.state.device) {
description = this.state.device.displayName; description = this.state.device.displayName;
detail = _t("%(deviceId)s from %(ip)s", { detail = _t("encryption|verification|request_toast_detail", {
deviceId: this.state.device.deviceId, deviceId: this.state.device.deviceId,
ip: this.state.ip, ip: this.state.ip,
}); });
@ -179,13 +179,13 @@ export default class VerificationRequestToast extends React.PureComponent<IProps
const declineLabel = const declineLabel =
this.state.counter === 0 this.state.counter === 0
? _t("action|ignore") ? _t("action|ignore")
: _t("Ignore (%(counter)s)", { counter: this.state.counter }); : _t("encryption|verification|request_toast_decline_counter", { counter: this.state.counter });
return ( return (
<GenericToast <GenericToast
description={description} description={description}
detail={detail} detail={detail}
acceptLabel={_t("Verify Session")} acceptLabel={_t("encryption|verification|request_toast_accept")}
onAccept={this.accept} onAccept={this.accept}
rejectLabel={declineLabel} rejectLabel={declineLabel}
onReject={this.cancel} onReject={this.cancel}

View file

@ -285,7 +285,7 @@ export const Lobby: FC<LobbyProps> = ({ room, joinCallButtonDisabledTooltip, con
disabled={connecting || joinCallButtonDisabledTooltip !== undefined} disabled={connecting || joinCallButtonDisabledTooltip !== undefined}
onClick={onConnectClick} onClick={onConnectClick}
label={_t("action|join")} label={_t("action|join")}
tooltip={connecting ? _t("Connecting") : joinCallButtonDisabledTooltip} tooltip={connecting ? _t("voip|connecting") : joinCallButtonDisabledTooltip}
alignment={Alignment.Bottom} alignment={Alignment.Bottom}
/> />
</div> </div>
@ -397,7 +397,7 @@ const JoinCallView: FC<JoinCallViewProps> = ({ room, resizing, call }) => {
facePile = ( facePile = (
<div className="mx_CallView_participants"> <div className="mx_CallView_participants">
{_t("%(count)s people joined", { count: members.length })} {_t("voip|n_people_joined", { count: members.length })}
<FacePile members={shownMembers} size="24px" overflow={overflow} /> <FacePile members={shownMembers} size="24px" overflow={overflow} />
</div> </div>
); );

View file

@ -440,10 +440,10 @@ export default class LegacyCallView extends React.Component<IProps, IState> {
const cli = MatrixClientPeg.safeGet(); const cli = MatrixClientPeg.safeGet();
const callRoomId = LegacyCallHandler.instance.roomIdForCall(call); const callRoomId = LegacyCallHandler.instance.roomIdForCall(call);
const transferTargetRoom = callRoomId ? cli.getRoom(callRoomId) : null; const transferTargetRoom = callRoomId ? cli.getRoom(callRoomId) : null;
const transferTargetName = transferTargetRoom ? transferTargetRoom.name : _t("unknown person"); const transferTargetName = transferTargetRoom ? transferTargetRoom.name : _t("voip|unknown_person");
const transfereeCallRoomId = LegacyCallHandler.instance.roomIdForCall(transfereeCall); const transfereeCallRoomId = LegacyCallHandler.instance.roomIdForCall(transfereeCall);
const transfereeRoom = transfereeCallRoomId ? cli.getRoom(transfereeCallRoomId) : null; const transfereeRoom = transfereeCallRoomId ? cli.getRoom(transfereeCallRoomId) : null;
const transfereeName = transfereeRoom ? transfereeRoom.name : _t("unknown person"); const transfereeName = transfereeRoom ? transfereeRoom.name : _t("voip|unknown_person");
holdTransferContent = ( holdTransferContent = (
<div className="mx_LegacyCallView_status"> <div className="mx_LegacyCallView_status">
@ -508,7 +508,7 @@ export default class LegacyCallView extends React.Component<IProps, IState> {
<RoomAvatar room={callRoom} size={avatarSize} /> <RoomAvatar room={callRoom} size={avatarSize} />
</div> </div>
</div> </div>
<div className="mx_LegacyCallView_status">{_t("Connecting")}</div> <div className="mx_LegacyCallView_status">{_t("voip|connecting")}</div>
{secondaryFeedElement} {secondaryFeedElement}
</div> </div>
); );

View file

@ -304,8 +304,8 @@ export default class LegacyCallViewButtons extends React.Component<IProps, IStat
<LegacyCallViewToggleButton <LegacyCallViewToggleButton
state={this.props.buttonsState.sidebarShown} state={this.props.buttonsState.sidebarShown}
className="mx_LegacyCallViewButtons_button_sidebar" className="mx_LegacyCallViewButtons_button_sidebar"
onLabel={_t("Hide sidebar")} onLabel={_t("voip|hide_sidebar_button")}
offLabel={_t("Show sidebar")} offLabel={_t("voip|show_sidebar_button")}
onClick={this.props.handlers.onToggleSidebarClick} onClick={this.props.handlers.onToggleSidebarClick}
/> />
)} )}
@ -315,7 +315,7 @@ export default class LegacyCallViewButtons extends React.Component<IProps, IStat
onClick={this.onMoreClick} onClick={this.onMoreClick}
inputRef={this.contextMenuButton} inputRef={this.contextMenuButton}
isExpanded={this.state.showMoreMenu} isExpanded={this.state.showMoreMenu}
title={_t("More")} title={_t("voip|more_button")}
alignment={Alignment.Top} alignment={Alignment.Top}
/> />
)} )}

View file

@ -143,7 +143,7 @@ export class Media {
public downloadSource(): Promise<Response> { public downloadSource(): Promise<Response> {
const src = this.srcHttp; const src = this.srcHttp;
if (!src) { if (!src) {
throw new UserFriendlyError("Failed to download source media, no source url was found"); throw new UserFriendlyError("error|download_media");
} }
return fetch(src); return fetch(src);
} }

View file

@ -190,16 +190,16 @@ export const useRoomCall = (
let videoCallDisabledReason: string | null; let videoCallDisabledReason: string | null;
switch (state) { switch (state) {
case State.NoPermission: case State.NoPermission:
voiceCallDisabledReason = _t("You do not have permission to start voice calls"); voiceCallDisabledReason = _t("voip|disabled_no_perms_start_voice_call");
videoCallDisabledReason = _t("You do not have permission to start video calls"); videoCallDisabledReason = _t("voip|disabled_no_perms_start_video_call");
break; break;
case State.Ongoing: case State.Ongoing:
voiceCallDisabledReason = _t("Ongoing call"); voiceCallDisabledReason = _t("voip|disabled_ongoing_call");
videoCallDisabledReason = _t("Ongoing call"); videoCallDisabledReason = _t("voip|disabled_ongoing_call");
break; break;
case State.NoOneHere: case State.NoOneHere:
voiceCallDisabledReason = _t("There's no one here to call"); voiceCallDisabledReason = _t("voip|disabled_no_one_here");
videoCallDisabledReason = _t("There's no one here to call"); videoCallDisabledReason = _t("voip|disabled_no_one_here");
break; break;
case State.Unpinned: case State.Unpinned:
case State.NoCall: case State.NoCall:

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