Merge branch 'develop' into germain-gg/notifications-labs
This commit is contained in:
commit
1ba419fe11
155 changed files with 17952 additions and 15335 deletions
|
@ -874,7 +874,7 @@ export class SSOAuthEntry extends React.Component<ISSOAuthEntryProps, ISSOAuthEn
|
|||
if (this.state.phase === SSOAuthEntry.PHASE_PREAUTH) {
|
||||
continueButton = (
|
||||
<AccessibleButton onClick={this.onStartAuthClick} kind={this.props.continueKind || "primary"}>
|
||||
{this.props.continueText || _t("Single Sign On")}
|
||||
{this.props.continueText || _t("auth|sso")}
|
||||
</AccessibleButton>
|
||||
);
|
||||
} else {
|
||||
|
|
|
@ -66,7 +66,7 @@ const LiveTimeRemaining: React.FC<{ beacon: Beacon }> = ({ beacon }) => {
|
|||
const msRemaining = useMsRemaining(beacon);
|
||||
|
||||
const timeRemaining = formatDuration(msRemaining);
|
||||
const liveTimeRemaining = _t(`%(timeRemaining)s left`, { timeRemaining });
|
||||
const liveTimeRemaining = _t("time|left", { timeRemaining });
|
||||
|
||||
return (
|
||||
<span data-testid="room-live-share-expiry" className="mx_LiveTimeRemaining">
|
||||
|
|
|
@ -45,8 +45,8 @@ interface IBetaPillProps {
|
|||
|
||||
export const BetaPill: React.FC<IBetaPillProps> = ({
|
||||
onClick,
|
||||
tooltipTitle = _t("This is a beta feature"),
|
||||
tooltipCaption = _t("Click for more info"),
|
||||
tooltipTitle = _t("labs|beta_feature"),
|
||||
tooltipCaption = _t("labs|click_for_info"),
|
||||
}) => {
|
||||
if (onClick) {
|
||||
return (
|
||||
|
@ -94,18 +94,16 @@ const BetaCard: React.FC<IProps> = ({ title: titleOverride, featureId }) => {
|
|||
let refreshWarning: string | undefined;
|
||||
if (requiresRefresh) {
|
||||
const brand = SdkConfig.get().brand;
|
||||
refreshWarning = value
|
||||
? _t("Leaving the beta will reload %(brand)s.", { brand })
|
||||
: _t("Joining the beta will reload %(brand)s.", { brand });
|
||||
refreshWarning = value ? _t("labs|leave_beta_reload", { brand }) : _t("labs|join_beta_reload", { brand });
|
||||
}
|
||||
|
||||
let content: ReactNode;
|
||||
if (busy) {
|
||||
content = <InlineSpinner />;
|
||||
} else if (value) {
|
||||
content = _t("Leave the beta");
|
||||
content = _t("labs|leave_beta");
|
||||
} else {
|
||||
content = _t("Join the beta");
|
||||
content = _t("labs|join_beta");
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -51,7 +51,7 @@ export const AnalyticsLearnMoreDialog: React.FC<IProps> = ({
|
|||
const privacyPolicyLink = privacyPolicyUrl ? (
|
||||
<span>
|
||||
{_t(
|
||||
"You can read all our terms <PrivacyPolicyUrl>here</PrivacyPolicyUrl>",
|
||||
"analytics|privacy_policy",
|
||||
{},
|
||||
{
|
||||
PrivacyPolicyUrl: (sub) => {
|
||||
|
@ -71,33 +71,18 @@ export const AnalyticsLearnMoreDialog: React.FC<IProps> = ({
|
|||
<BaseDialog
|
||||
className="mx_AnalyticsLearnMoreDialog"
|
||||
contentId="mx_AnalyticsLearnMore"
|
||||
title={_t("Help improve %(analyticsOwner)s", { analyticsOwner })}
|
||||
title={_t("analytics|enable_prompt", { analyticsOwner })}
|
||||
onFinished={onFinished}
|
||||
>
|
||||
<div className="mx_Dialog_content">
|
||||
<div className="mx_AnalyticsLearnMore_image_holder" />
|
||||
<div className="mx_AnalyticsLearnMore_copy">
|
||||
{_t(
|
||||
"Help us identify issues and improve %(analyticsOwner)s by sharing anonymous usage data. To understand how people use multiple devices, we'll generate a random identifier, shared by your devices.",
|
||||
{ analyticsOwner },
|
||||
)}
|
||||
{_t("analytics|pseudonymous_usage_data", { analyticsOwner })}
|
||||
</div>
|
||||
<ul className="mx_AnalyticsLearnMore_bullets">
|
||||
<li>
|
||||
{_t(
|
||||
"We <Bold>don't</Bold> record or profile any account data",
|
||||
{},
|
||||
{ Bold: (sub) => <b>{sub}</b> },
|
||||
)}
|
||||
</li>
|
||||
<li>
|
||||
{_t(
|
||||
"We <Bold>don't</Bold> share information with third parties",
|
||||
{},
|
||||
{ Bold: (sub) => <b>{sub}</b> },
|
||||
)}
|
||||
</li>
|
||||
<li>{_t("You can turn this off anytime in settings")}</li>
|
||||
<li>{_t("analytics|bullet_1", {}, { Bold: (sub) => <b>{sub}</b> })}</li>
|
||||
<li>{_t("analytics|bullet_2", {}, { Bold: (sub) => <b>{sub}</b> })}</li>
|
||||
<li>{_t("analytics|disable_prompt")}</li>
|
||||
</ul>
|
||||
{privacyPolicyLink}
|
||||
</div>
|
||||
|
|
|
@ -47,14 +47,14 @@ export const AppDownloadDialog: FC<Props> = ({ onFinished }) => {
|
|||
|
||||
return (
|
||||
<BaseDialog
|
||||
title={_t("Download %(brand)s", { brand })}
|
||||
title={_t("onboarding|download_brand", { brand })}
|
||||
className="mx_AppDownloadDialog"
|
||||
fixedWidth
|
||||
onFinished={onFinished}
|
||||
>
|
||||
{desktopBuilds?.get("available") && (
|
||||
<div className="mx_AppDownloadDialog_desktop">
|
||||
<Heading size="3">{_t("Download %(brand)s Desktop", { brand })}</Heading>
|
||||
<Heading size="3">{_t("onboarding|download_brand_desktop", { brand })}</Heading>
|
||||
<AccessibleButton
|
||||
kind="primary"
|
||||
element="a"
|
||||
|
@ -62,7 +62,7 @@ export const AppDownloadDialog: FC<Props> = ({ onFinished }) => {
|
|||
target="_blank"
|
||||
onClick={() => {}}
|
||||
>
|
||||
{_t("Download %(brand)s Desktop", { brand })}
|
||||
{_t("onboarding|download_brand_desktop", { brand })}
|
||||
</AccessibleButton>
|
||||
</div>
|
||||
)}
|
||||
|
@ -71,7 +71,7 @@ export const AppDownloadDialog: FC<Props> = ({ onFinished }) => {
|
|||
<Heading size="3">{_t("common|ios")}</Heading>
|
||||
<QRCode data={urlAppStore} margin={0} width={172} />
|
||||
<div className="mx_AppDownloadDialog_info">
|
||||
{_t("%(qrCode)s or %(appLinks)s", {
|
||||
{_t("onboarding|qr_or_app_links", {
|
||||
appLinks: "",
|
||||
qrCode: "",
|
||||
})}
|
||||
|
@ -81,7 +81,7 @@ export const AppDownloadDialog: FC<Props> = ({ onFinished }) => {
|
|||
element="a"
|
||||
href={urlAppStore}
|
||||
target="_blank"
|
||||
aria-label={_t("Download on the App Store")}
|
||||
aria-label={_t("onboarding|download_app_store")}
|
||||
onClick={() => {}}
|
||||
>
|
||||
<IOSBadge />
|
||||
|
@ -92,7 +92,7 @@ export const AppDownloadDialog: FC<Props> = ({ onFinished }) => {
|
|||
<Heading size="3">{_t("common|android")}</Heading>
|
||||
<QRCode data={urlAndroid} margin={0} width={172} />
|
||||
<div className="mx_AppDownloadDialog_info">
|
||||
{_t("%(qrCode)s or %(appLinks)s", {
|
||||
{_t("onboarding|qr_or_app_links", {
|
||||
appLinks: "",
|
||||
qrCode: "",
|
||||
})}
|
||||
|
@ -102,7 +102,7 @@ export const AppDownloadDialog: FC<Props> = ({ onFinished }) => {
|
|||
element="a"
|
||||
href={urlGooglePlay}
|
||||
target="_blank"
|
||||
aria-label={_t("Get it on Google Play")}
|
||||
aria-label={_t("onboarding|download_google_play")}
|
||||
onClick={() => {}}
|
||||
>
|
||||
<GooglePlayBadge />
|
||||
|
@ -111,7 +111,7 @@ export const AppDownloadDialog: FC<Props> = ({ onFinished }) => {
|
|||
element="a"
|
||||
href={urlFDroid}
|
||||
target="_blank"
|
||||
aria-label={_t("Get it on F-Droid")}
|
||||
aria-label={_t("onboarding|download_f_droid")}
|
||||
onClick={() => {}}
|
||||
>
|
||||
<FDroidBadge />
|
||||
|
@ -120,8 +120,8 @@ export const AppDownloadDialog: FC<Props> = ({ onFinished }) => {
|
|||
</div>
|
||||
</div>
|
||||
<div className="mx_AppDownloadDialog_legal">
|
||||
<p>{_t("App Store® and the Apple logo® are trademarks of Apple Inc.")}</p>
|
||||
<p>{_t("Google Play and the Google Play logo are trademarks of Google LLC.")}</p>
|
||||
<p>{_t("onboarding|apple_trademarks")}</p>
|
||||
<p>{_t("onboarding|google_trademarks")}</p>
|
||||
</div>
|
||||
</BaseDialog>
|
||||
);
|
||||
|
|
|
@ -74,7 +74,7 @@ export default class DeactivateAccountDialog extends React.Component<IProps, ISt
|
|||
const dialogAesthetics = {
|
||||
[SSOAuthEntry.PHASE_PREAUTH]: {
|
||||
body: _t("Confirm your account deactivation by using Single Sign On to prove your identity."),
|
||||
continueText: _t("Single Sign On"),
|
||||
continueText: _t("auth|sso"),
|
||||
continueKind: "danger",
|
||||
},
|
||||
[SSOAuthEntry.PHASE_POSTAUTH]: {
|
||||
|
|
|
@ -106,7 +106,7 @@ const ExportDialog: React.FC<IProps> = ({ room, onFinished }) => {
|
|||
const [isExporting, setExporting] = useState(false);
|
||||
const sizeLimitRef = useRef<Field>(null);
|
||||
const messageCountRef = useRef<Field>(null);
|
||||
const [exportProgressText, setExportProgressText] = useState(_t("Processing…"));
|
||||
const [exportProgressText, setExportProgressText] = useState(_t("export_chat|processing"));
|
||||
const [displayCancel, setCancelWarning] = useState(false);
|
||||
const [exportCancelled, setExportCancelled] = useState(false);
|
||||
const [exportSuccessful, setExportSuccessful] = useState(false);
|
||||
|
@ -173,7 +173,7 @@ const ExportDialog: React.FC<IProps> = ({ room, onFinished }) => {
|
|||
invalid: () => {
|
||||
const min = 1;
|
||||
const max = 2000;
|
||||
return _t("Enter a number between %(min)s and %(max)s", {
|
||||
return _t("export_chat|enter_number_between_min_max", {
|
||||
min,
|
||||
max,
|
||||
});
|
||||
|
@ -188,7 +188,7 @@ const ExportDialog: React.FC<IProps> = ({ room, onFinished }) => {
|
|||
invalid: () => {
|
||||
const min = 1;
|
||||
const max = 2000;
|
||||
return _t("Size can only be a number between %(min)s MB and %(max)s MB", { min, max });
|
||||
return _t("export_chat|size_limit_min_max", { min, max });
|
||||
},
|
||||
},
|
||||
],
|
||||
|
@ -209,7 +209,7 @@ const ExportDialog: React.FC<IProps> = ({ room, onFinished }) => {
|
|||
invalid: () => {
|
||||
const min = 1;
|
||||
const max = 10 ** 8;
|
||||
return _t("Enter a number between %(min)s and %(max)s", {
|
||||
return _t("export_chat|enter_number_between_min_max", {
|
||||
min,
|
||||
max,
|
||||
});
|
||||
|
@ -224,7 +224,7 @@ const ExportDialog: React.FC<IProps> = ({ room, onFinished }) => {
|
|||
invalid: () => {
|
||||
const min = 1;
|
||||
const max = 10 ** 8;
|
||||
return _t("Number of messages can only be a number between %(min)s and %(max)s", { min, max });
|
||||
return _t("export_chat|num_messages_min_max", { min, max });
|
||||
},
|
||||
},
|
||||
],
|
||||
|
@ -270,7 +270,7 @@ const ExportDialog: React.FC<IProps> = ({ room, onFinished }) => {
|
|||
value={numberOfMessages.toString()}
|
||||
ref={messageCountRef}
|
||||
onValidate={onValidateNumberOfMessages}
|
||||
label={_t("Number of messages")}
|
||||
label={_t("export_chat|num_messages")}
|
||||
onChange={(e) => {
|
||||
setNumberOfMessages(parseInt(e.target.value));
|
||||
}}
|
||||
|
@ -284,8 +284,8 @@ const ExportDialog: React.FC<IProps> = ({ room, onFinished }) => {
|
|||
// Display successful cancellation message
|
||||
return (
|
||||
<InfoDialog
|
||||
title={_t("Export Cancelled")}
|
||||
description={_t("The export was cancelled successfully")}
|
||||
title={_t("export_chat|cancelled")}
|
||||
description={_t("export_chat|cancelled_detail")}
|
||||
hasCloseButton={true}
|
||||
onFinished={onFinished}
|
||||
/>
|
||||
|
@ -294,8 +294,8 @@ const ExportDialog: React.FC<IProps> = ({ room, onFinished }) => {
|
|||
// Display successful export message
|
||||
return (
|
||||
<InfoDialog
|
||||
title={_t("Export Successful")}
|
||||
description={_t("Your export was successful. Find it in your Downloads folder.")}
|
||||
title={_t("export_chat|successful")}
|
||||
description={_t("export_chat|successful_detail")}
|
||||
hasCloseButton={true}
|
||||
onFinished={onFinished}
|
||||
/>
|
||||
|
@ -310,7 +310,7 @@ const ExportDialog: React.FC<IProps> = ({ room, onFinished }) => {
|
|||
onFinished={onFinished}
|
||||
fixedWidth={true}
|
||||
>
|
||||
<p>{_t("Are you sure you want to stop exporting your data? If you do, you'll need to start over.")}</p>
|
||||
<p>{_t("export_chat|confirm_stop")}</p>
|
||||
<DialogButtons
|
||||
primaryButton={_t("action|stop")}
|
||||
primaryButtonClass="danger"
|
||||
|
@ -325,19 +325,19 @@ const ExportDialog: React.FC<IProps> = ({ room, onFinished }) => {
|
|||
// Display export settings
|
||||
return (
|
||||
<BaseDialog
|
||||
title={isExporting ? _t("Exporting your data") : _t("Export Chat")}
|
||||
title={isExporting ? _t("export_chat|exporting_your_data") : _t("export_chat|title")}
|
||||
className={`mx_ExportDialog ${isExporting && "mx_ExportDialog_Exporting"}`}
|
||||
contentId="mx_Dialog_content"
|
||||
hasCancel={true}
|
||||
onFinished={onFinished}
|
||||
fixedWidth={true}
|
||||
>
|
||||
{!isExporting ? <p>{_t("Select from the options below to export chats from your timeline")}</p> : null}
|
||||
{!isExporting ? <p>{_t("export_chat|select_option")}</p> : null}
|
||||
|
||||
<div className="mx_ExportDialog_options">
|
||||
{!!setExportFormat && (
|
||||
<>
|
||||
<span className="mx_ExportDialog_subheading">{_t("Format")}</span>
|
||||
<span className="mx_ExportDialog_subheading">{_t("export_chat|format")}</span>
|
||||
|
||||
<StyledRadioGroup
|
||||
name="exportFormat"
|
||||
|
@ -350,7 +350,7 @@ const ExportDialog: React.FC<IProps> = ({ room, onFinished }) => {
|
|||
|
||||
{!!setExportType && (
|
||||
<>
|
||||
<span className="mx_ExportDialog_subheading">{_t("Messages")}</span>
|
||||
<span className="mx_ExportDialog_subheading">{_t("export_chat|messages")}</span>
|
||||
|
||||
<Field
|
||||
id="export-type"
|
||||
|
@ -368,7 +368,7 @@ const ExportDialog: React.FC<IProps> = ({ room, onFinished }) => {
|
|||
|
||||
{setSizeLimit && (
|
||||
<>
|
||||
<span className="mx_ExportDialog_subheading">{_t("Size Limit")}</span>
|
||||
<span className="mx_ExportDialog_subheading">{_t("export_chat|size_limit")}</span>
|
||||
|
||||
<Field
|
||||
id="size-limit"
|
||||
|
@ -392,7 +392,7 @@ const ExportDialog: React.FC<IProps> = ({ room, onFinished }) => {
|
|||
checked={includeAttachments}
|
||||
onChange={(e) => setAttachments((e.target as HTMLInputElement).checked)}
|
||||
>
|
||||
{_t("Include Attachments")}
|
||||
{_t("export_chat|include_attachments")}
|
||||
</StyledCheckbox>
|
||||
</>
|
||||
)}
|
||||
|
|
|
@ -99,7 +99,7 @@ export default class InteractiveAuthDialog<T> extends React.Component<Interactiv
|
|||
[SSOAuthEntry.PHASE_PREAUTH]: {
|
||||
title: _t("Use Single Sign On to continue"),
|
||||
body: _t("To continue, use Single Sign On to prove your identity."),
|
||||
continueText: _t("Single Sign On"),
|
||||
continueText: _t("auth|sso"),
|
||||
continueKind: "primary",
|
||||
},
|
||||
[SSOAuthEntry.PHASE_POSTAUTH]: {
|
||||
|
|
|
@ -216,7 +216,7 @@ export default class ReportEventDialog extends React.Component<IProps, IState> {
|
|||
((this.state.nature == Nature.Other || this.state.nature == NonStandardValue.Admin) && !reason)
|
||||
) {
|
||||
this.setState({
|
||||
err: _t("Please fill why you're reporting."),
|
||||
err: _t("report_content|missing_reason"),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
@ -225,7 +225,7 @@ export default class ReportEventDialog extends React.Component<IProps, IState> {
|
|||
// We need a `reason`.
|
||||
if (!reason) {
|
||||
this.setState({
|
||||
err: _t("Please fill why you're reporting."),
|
||||
err: _t("report_content|missing_reason"),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
@ -295,8 +295,8 @@ export default class ReportEventDialog extends React.Component<IProps, IState> {
|
|||
const ignoreUserCheckbox = (
|
||||
<LabelledCheckbox
|
||||
value={this.state.ignoreUserToo}
|
||||
label={_t("Ignore user")}
|
||||
byline={_t("Check if you want to hide all current and future messages from this user.")}
|
||||
label={_t("report_content|ignore_user")}
|
||||
byline={_t("report_content|hide_messages_from_user")}
|
||||
onChange={this.onIgnoreUserTooChanged}
|
||||
disabled={this.state.busy}
|
||||
/>
|
||||
|
@ -317,7 +317,7 @@ export default class ReportEventDialog extends React.Component<IProps, IState> {
|
|||
let subtitle: string;
|
||||
switch (this.state.nature) {
|
||||
case Nature.Disagreement:
|
||||
subtitle = _t("What this user is writing is wrong.\nThis will be reported to the room moderators.");
|
||||
subtitle = _t("report_content|nature_disagreement");
|
||||
break;
|
||||
case Nature.Toxic:
|
||||
subtitle = _t(
|
||||
|
@ -353,7 +353,7 @@ export default class ReportEventDialog extends React.Component<IProps, IState> {
|
|||
);
|
||||
break;
|
||||
default:
|
||||
subtitle = _t("Please pick a nature and describe what makes this message abusive.");
|
||||
subtitle = _t("report_content|nature");
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -371,7 +371,7 @@ export default class ReportEventDialog extends React.Component<IProps, IState> {
|
|||
checked={this.state.nature == Nature.Disagreement}
|
||||
onChange={this.onNatureChosen}
|
||||
>
|
||||
{_t("Disagree")}
|
||||
{_t("report_content|disagree")}
|
||||
</StyledRadioButton>
|
||||
<StyledRadioButton
|
||||
name="nature"
|
||||
|
@ -379,7 +379,7 @@ export default class ReportEventDialog extends React.Component<IProps, IState> {
|
|||
checked={this.state.nature == Nature.Toxic}
|
||||
onChange={this.onNatureChosen}
|
||||
>
|
||||
{_t("Toxic Behaviour")}
|
||||
{_t("report_content|toxic_behaviour")}
|
||||
</StyledRadioButton>
|
||||
<StyledRadioButton
|
||||
name="nature"
|
||||
|
@ -387,7 +387,7 @@ export default class ReportEventDialog extends React.Component<IProps, IState> {
|
|||
checked={this.state.nature == Nature.Illegal}
|
||||
onChange={this.onNatureChosen}
|
||||
>
|
||||
{_t("Illegal Content")}
|
||||
{_t("report_content|illegal_content")}
|
||||
</StyledRadioButton>
|
||||
<StyledRadioButton
|
||||
name="nature"
|
||||
|
@ -395,7 +395,7 @@ export default class ReportEventDialog extends React.Component<IProps, IState> {
|
|||
checked={this.state.nature == Nature.Spam}
|
||||
onChange={this.onNatureChosen}
|
||||
>
|
||||
{_t("Spam or propaganda")}
|
||||
{_t("report_content|spam_or_propaganda")}
|
||||
</StyledRadioButton>
|
||||
<StyledRadioButton
|
||||
name="nature"
|
||||
|
@ -403,7 +403,7 @@ export default class ReportEventDialog extends React.Component<IProps, IState> {
|
|||
checked={this.state.nature == NonStandardValue.Admin}
|
||||
onChange={this.onNatureChosen}
|
||||
>
|
||||
{_t("Report the entire room")}
|
||||
{_t("report_content|report_entire_room")}
|
||||
</StyledRadioButton>
|
||||
<StyledRadioButton
|
||||
name="nature"
|
||||
|
@ -443,7 +443,7 @@ export default class ReportEventDialog extends React.Component<IProps, IState> {
|
|||
<BaseDialog
|
||||
className="mx_ReportEventDialog"
|
||||
onFinished={this.props.onFinished}
|
||||
title={_t("Report Content to Your Homeserver Administrator")}
|
||||
title={_t("report_content|report_content_to_homeserver")}
|
||||
contentId="mx_ReportEventDialog"
|
||||
>
|
||||
<div className="mx_ReportEventDialog" id="mx_ReportEventDialog">
|
||||
|
|
|
@ -67,7 +67,7 @@ export default class SessionRestoreErrorDialog extends React.Component<IProps> {
|
|||
if (SdkConfig.get().bug_report_endpoint_url) {
|
||||
dialogButtons = (
|
||||
<DialogButtons
|
||||
primaryButton={_t("Send Logs")}
|
||||
primaryButton={_t("bug_reporting|send_logs")}
|
||||
onPrimaryButtonClick={this.sendBugReport}
|
||||
focus={true}
|
||||
hasCancel={false}
|
||||
|
|
|
@ -92,14 +92,14 @@ export default class TermsDialog extends React.PureComponent<ITermsDialogProps,
|
|||
case SERVICE_TYPES.IS:
|
||||
return (
|
||||
<div>
|
||||
{_t("Identity server")}
|
||||
{_t("common|identity_server")}
|
||||
<br />({host})
|
||||
</div>
|
||||
);
|
||||
case SERVICE_TYPES.IM:
|
||||
return (
|
||||
<div>
|
||||
{_t("Integration manager")}
|
||||
{_t("common|integration_manager")}
|
||||
<br />({host})
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -51,7 +51,7 @@ export default class UploadFailureDialog extends React.Component<IProps> {
|
|||
message = _t(
|
||||
"This file is <b>too large</b> to upload. The file size limit is %(limit)s but this file is %(sizeOfThisFile)s.",
|
||||
{
|
||||
limit: fileSize(this.props.contentMessages.getUploadLimit()),
|
||||
limit: fileSize(this.props.contentMessages.getUploadLimit()!),
|
||||
sizeOfThisFile: fileSize(this.props.badFiles[0].size),
|
||||
},
|
||||
{
|
||||
|
@ -70,7 +70,7 @@ export default class UploadFailureDialog extends React.Component<IProps> {
|
|||
message = _t(
|
||||
"These files are <b>too large</b> to upload. The file size limit is %(limit)s.",
|
||||
{
|
||||
limit: fileSize(this.props.contentMessages.getUploadLimit()),
|
||||
limit: fileSize(this.props.contentMessages.getUploadLimit()!),
|
||||
},
|
||||
{
|
||||
b: (sub) => <b>{sub}</b>,
|
||||
|
@ -88,7 +88,7 @@ export default class UploadFailureDialog extends React.Component<IProps> {
|
|||
message = _t(
|
||||
"Some files are <b>too large</b> to be uploaded. The file size limit is %(limit)s.",
|
||||
{
|
||||
limit: fileSize(this.props.contentMessages.getUploadLimit()),
|
||||
limit: fileSize(this.props.contentMessages.getUploadLimit()!),
|
||||
},
|
||||
{
|
||||
b: (sub) => <b>{sub}</b>,
|
||||
|
|
|
@ -189,7 +189,7 @@ export default class UserSettingsDialog extends React.Component<IProps, IState>
|
|||
tabs.push(
|
||||
new Tab(
|
||||
UserTab.Help,
|
||||
_td("Help & About"),
|
||||
_td("setting|help_about|title"),
|
||||
"mx_UserSettingsDialog_helpIcon",
|
||||
<HelpUserSettingsTab closeSettingsFn={() => this.props.onFinished()} />,
|
||||
"UserSettingsHelpAbout",
|
||||
|
|
|
@ -114,7 +114,7 @@ export default class CreateCrossSigningDialog extends React.PureComponent<IProps
|
|||
[SSOAuthEntry.PHASE_PREAUTH]: {
|
||||
title: _t("Use Single Sign On to continue"),
|
||||
body: _t("To continue, use Single Sign On to prove your identity."),
|
||||
continueText: _t("Single Sign On"),
|
||||
continueText: _t("auth|sso"),
|
||||
continueKind: "primary",
|
||||
},
|
||||
[SSOAuthEntry.PHASE_POSTAUTH]: {
|
||||
|
|
|
@ -106,7 +106,7 @@ export function RoomResultContextMenus({ room }: Props): JSX.Element {
|
|||
const target = ev.target as HTMLElement;
|
||||
setNotificationMenuPosition(target.getBoundingClientRect());
|
||||
}}
|
||||
title={_t("Notification options")}
|
||||
title={_t("room_list|notification_options")}
|
||||
isExpanded={notificationMenuPosition !== null}
|
||||
/>
|
||||
)}
|
||||
|
|
|
@ -263,15 +263,15 @@ const findVisibleRoomMembers = (visibleRooms: Room[], cli: MatrixClient, filterD
|
|||
|
||||
const roomAriaUnreadLabel = (room: Room, notification: RoomNotificationState): string | undefined => {
|
||||
if (notification.hasMentions) {
|
||||
return _t("%(count)s unread messages including mentions.", {
|
||||
return _t("a11y|n_unread_messages_mentions", {
|
||||
count: notification.count,
|
||||
});
|
||||
} else if (notification.hasUnreadCount) {
|
||||
return _t("%(count)s unread messages.", {
|
||||
return _t("a11y|n_unread_messages", {
|
||||
count: notification.count,
|
||||
});
|
||||
} else if (notification.isUnread) {
|
||||
return _t("Unread messages.");
|
||||
return _t("a11y|unread_messages");
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
|
@ -563,7 +563,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
|
|||
};
|
||||
|
||||
let otherSearchesSection: JSX.Element | undefined;
|
||||
if (trimmedQuery || filter !== Filter.PublicRooms) {
|
||||
if (trimmedQuery || (filter !== Filter.PublicRooms && filter !== Filter.PublicSpaces)) {
|
||||
otherSearchesSection = (
|
||||
<div
|
||||
className="mx_SpotlightDialog_section mx_SpotlightDialog_otherSearches"
|
||||
|
|
|
@ -85,7 +85,7 @@ export default class ErrorBoundary extends React.PureComponent<Props, IState> {
|
|||
<React.Fragment>
|
||||
<p>
|
||||
{_t(
|
||||
"Please <newIssueLink>create a new issue</newIssueLink> on GitHub so that we can investigate this bug.",
|
||||
"bug_reporting|create_new_issue",
|
||||
{},
|
||||
{
|
||||
newIssueLink: (sub) => {
|
||||
|
@ -115,7 +115,7 @@ export default class ErrorBoundary extends React.PureComponent<Props, IState> {
|
|||
if (MatrixClientPeg.get()) {
|
||||
clearCacheButton = (
|
||||
<AccessibleButton onClick={this.onClearCacheAndReload} kind="danger">
|
||||
{_t("Clear cache and reload")}
|
||||
{_t("setting|help_about|clear_cache_reload")}
|
||||
</AccessibleButton>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -133,7 +133,7 @@ export default class EventListSummary extends React.Component<
|
|||
|
||||
const desc = formatCommaSeparatedList(descs);
|
||||
|
||||
return _t("%(nameList)s %(transitionList)s", { nameList, transitionList: desc });
|
||||
return _t("timeline|summary|format", { nameList, transitionList: desc });
|
||||
});
|
||||
|
||||
if (!summaries) {
|
||||
|
@ -250,101 +250,101 @@ export default class EventListSummary extends React.Component<
|
|||
case TransitionType.Joined:
|
||||
res =
|
||||
userCount > 1
|
||||
? _t("%(severalUsers)sjoined %(count)s times", { severalUsers: "", count })
|
||||
: _t("%(oneUser)sjoined %(count)s times", { oneUser: "", count });
|
||||
? _t("timeline|summary|joined_multiple", { severalUsers: "", count })
|
||||
: _t("timeline|summary|joined", { oneUser: "", count });
|
||||
break;
|
||||
case TransitionType.Left:
|
||||
res =
|
||||
userCount > 1
|
||||
? _t("%(severalUsers)sleft %(count)s times", { severalUsers: "", count })
|
||||
: _t("%(oneUser)sleft %(count)s times", { oneUser: "", count });
|
||||
? _t("timeline|summary|left_multiple", { severalUsers: "", count })
|
||||
: _t("timeline|summary|left", { oneUser: "", count });
|
||||
break;
|
||||
case TransitionType.JoinedAndLeft:
|
||||
res =
|
||||
userCount > 1
|
||||
? _t("%(severalUsers)sjoined and left %(count)s times", { severalUsers: "", count })
|
||||
: _t("%(oneUser)sjoined and left %(count)s times", { oneUser: "", count });
|
||||
? _t("timeline|summary|joined_and_left_multiple", { severalUsers: "", count })
|
||||
: _t("timeline|summary|joined_and_left", { oneUser: "", count });
|
||||
break;
|
||||
case TransitionType.LeftAndJoined:
|
||||
res =
|
||||
userCount > 1
|
||||
? _t("%(severalUsers)sleft and rejoined %(count)s times", { severalUsers: "", count })
|
||||
: _t("%(oneUser)sleft and rejoined %(count)s times", { oneUser: "", count });
|
||||
? _t("timeline|summary|rejoined_multiple", { severalUsers: "", count })
|
||||
: _t("timeline|summary|rejoined", { oneUser: "", count });
|
||||
break;
|
||||
case TransitionType.InviteReject:
|
||||
res =
|
||||
userCount > 1
|
||||
? _t("%(severalUsers)srejected their invitations %(count)s times", {
|
||||
? _t("timeline|summary|rejected_invite_multiple", {
|
||||
severalUsers: "",
|
||||
count,
|
||||
})
|
||||
: _t("%(oneUser)srejected their invitation %(count)s times", { oneUser: "", count });
|
||||
: _t("timeline|summary|rejected_invite", { oneUser: "", count });
|
||||
break;
|
||||
case TransitionType.InviteWithdrawal:
|
||||
res =
|
||||
userCount > 1
|
||||
? _t("%(severalUsers)shad their invitations withdrawn %(count)s times", {
|
||||
? _t("timeline|summary|invite_withdrawn_multiple", {
|
||||
severalUsers: "",
|
||||
count,
|
||||
})
|
||||
: _t("%(oneUser)shad their invitation withdrawn %(count)s times", { oneUser: "", count });
|
||||
: _t("timeline|summary|invite_withdrawn", { oneUser: "", count });
|
||||
break;
|
||||
case TransitionType.Invited:
|
||||
res =
|
||||
userCount > 1
|
||||
? _t("were invited %(count)s times", { count })
|
||||
: _t("was invited %(count)s times", { count });
|
||||
? _t("timeline|summary|invited_multiple", { count })
|
||||
: _t("timeline|summary|invited", { count });
|
||||
break;
|
||||
case TransitionType.Banned:
|
||||
res =
|
||||
userCount > 1
|
||||
? _t("were banned %(count)s times", { count })
|
||||
: _t("was banned %(count)s times", { count });
|
||||
? _t("timeline|summary|banned_multiple", { count })
|
||||
: _t("timeline|summary|banned", { count });
|
||||
break;
|
||||
case TransitionType.Unbanned:
|
||||
res =
|
||||
userCount > 1
|
||||
? _t("were unbanned %(count)s times", { count })
|
||||
: _t("was unbanned %(count)s times", { count });
|
||||
? _t("timeline|summary|unbanned_multiple", { count })
|
||||
: _t("timeline|summary|unbanned", { count });
|
||||
break;
|
||||
case TransitionType.Kicked:
|
||||
res =
|
||||
userCount > 1
|
||||
? _t("were removed %(count)s times", { count })
|
||||
: _t("was removed %(count)s times", { count });
|
||||
? _t("timeline|summary|kicked_multiple", { count })
|
||||
: _t("timeline|summary|kicked", { count });
|
||||
break;
|
||||
case TransitionType.ChangedName:
|
||||
res =
|
||||
userCount > 1
|
||||
? _t("%(severalUsers)schanged their name %(count)s times", { severalUsers: "", count })
|
||||
: _t("%(oneUser)schanged their name %(count)s times", { oneUser: "", count });
|
||||
? _t("timeline|summary|changed_name_multiple", { severalUsers: "", count })
|
||||
: _t("timeline|summary|changed_name", { oneUser: "", count });
|
||||
break;
|
||||
case TransitionType.ChangedAvatar:
|
||||
res =
|
||||
userCount > 1
|
||||
? _t("%(severalUsers)schanged their profile picture %(count)s times", {
|
||||
? _t("timeline|summary|changed_avatar_multiple", {
|
||||
severalUsers: "",
|
||||
count,
|
||||
})
|
||||
: _t("%(oneUser)schanged their profile picture %(count)s times", { oneUser: "", count });
|
||||
: _t("timeline|summary|changed_avatar", { oneUser: "", count });
|
||||
break;
|
||||
case TransitionType.NoChange:
|
||||
res =
|
||||
userCount > 1
|
||||
? _t("%(severalUsers)smade no changes %(count)s times", { severalUsers: "", count })
|
||||
: _t("%(oneUser)smade no changes %(count)s times", { oneUser: "", count });
|
||||
? _t("timeline|summary|no_change_multiple", { severalUsers: "", count })
|
||||
: _t("timeline|summary|no_change", { oneUser: "", count });
|
||||
break;
|
||||
case TransitionType.ServerAcl:
|
||||
res =
|
||||
userCount > 1
|
||||
? _t("%(severalUsers)schanged the server ACLs %(count)s times", { severalUsers: "", count })
|
||||
: _t("%(oneUser)schanged the server ACLs %(count)s times", { oneUser: "", count });
|
||||
? _t("timeline|summary|server_acls_multiple", { severalUsers: "", count })
|
||||
: _t("timeline|summary|server_acls", { oneUser: "", count });
|
||||
break;
|
||||
case TransitionType.ChangedPins:
|
||||
res =
|
||||
userCount > 1
|
||||
? _t(
|
||||
"%(severalUsers)schanged the <a>pinned messages</a> for the room %(count)s times",
|
||||
"timeline|summary|pinned_events_multiple",
|
||||
{ severalUsers: "", count },
|
||||
{
|
||||
a: (sub) => (
|
||||
|
@ -355,7 +355,7 @@ export default class EventListSummary extends React.Component<
|
|||
},
|
||||
)
|
||||
: _t(
|
||||
"%(oneUser)schanged the <a>pinned messages</a> for the room %(count)s times",
|
||||
"timeline|summary|pinned_events",
|
||||
{ oneUser: "", count },
|
||||
{
|
||||
a: (sub) => (
|
||||
|
@ -369,14 +369,14 @@ export default class EventListSummary extends React.Component<
|
|||
case TransitionType.MessageRemoved:
|
||||
res =
|
||||
userCount > 1
|
||||
? _t("%(severalUsers)sremoved a message %(count)s times", { severalUsers: "", count })
|
||||
: _t("%(oneUser)sremoved a message %(count)s times", { oneUser: "", count });
|
||||
? _t("timeline|summary|redacted_multiple", { severalUsers: "", count })
|
||||
: _t("timeline|summary|redacted", { oneUser: "", count });
|
||||
break;
|
||||
case TransitionType.HiddenEvent:
|
||||
res =
|
||||
userCount > 1
|
||||
? _t("%(severalUsers)ssent %(count)s hidden messages", { severalUsers: "", count })
|
||||
: _t("%(oneUser)ssent %(count)s hidden messages", { oneUser: "", count });
|
||||
? _t("timeline|summary|hidden_event_multiple", { severalUsers: "", count })
|
||||
: _t("timeline|summary|hidden_event", { oneUser: "", count });
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ const QRCode: React.FC<IProps> = ({ data, className, ...options }) => {
|
|||
|
||||
return (
|
||||
<div className={classNames("mx_QRCode", className)}>
|
||||
{dataUri ? <img src={dataUri} className="mx_VerificationQRCode" alt={_t("QR Code")} /> : <Spinner />}
|
||||
{dataUri ? <img src={dataUri} className="mx_VerificationQRCode" alt={_t("common|qr_code")} /> : <Spinner />}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -92,11 +92,11 @@ const SSOButton: React.FC<ISSOButtonProps> = ({
|
|||
}) => {
|
||||
let label: string;
|
||||
if (idp) {
|
||||
label = _t("Continue with %(provider)s", { provider: idp.name });
|
||||
label = _t("auth|continue_with_idp", { provider: idp.name });
|
||||
} else if (DELEGATED_OIDC_COMPATIBILITY.findIn<boolean>(flow)) {
|
||||
label = _t("action|continue");
|
||||
} else {
|
||||
label = _t("Sign in with single sign-on");
|
||||
label = _t("auth|sign_in_with_sso");
|
||||
}
|
||||
|
||||
const onClick = (): void => {
|
||||
|
|
|
@ -87,63 +87,63 @@ class EmojiPicker extends React.Component<IProps, IState> {
|
|||
this.categories = [
|
||||
{
|
||||
id: "recent",
|
||||
name: _t("Frequently Used"),
|
||||
name: _t("emoji|category_frequently_used"),
|
||||
enabled: this.recentlyUsed.length > 0,
|
||||
visible: this.recentlyUsed.length > 0,
|
||||
ref: React.createRef(),
|
||||
},
|
||||
{
|
||||
id: "people",
|
||||
name: _t("Smileys & People"),
|
||||
name: _t("emoji|category_smileys_people"),
|
||||
enabled: true,
|
||||
visible: true,
|
||||
ref: React.createRef(),
|
||||
},
|
||||
{
|
||||
id: "nature",
|
||||
name: _t("Animals & Nature"),
|
||||
name: _t("emoji|category_animals_nature"),
|
||||
enabled: true,
|
||||
visible: false,
|
||||
ref: React.createRef(),
|
||||
},
|
||||
{
|
||||
id: "foods",
|
||||
name: _t("Food & Drink"),
|
||||
name: _t("emoji|category_food_drink"),
|
||||
enabled: true,
|
||||
visible: false,
|
||||
ref: React.createRef(),
|
||||
},
|
||||
{
|
||||
id: "activity",
|
||||
name: _t("Activities"),
|
||||
name: _t("emoji|category_activities"),
|
||||
enabled: true,
|
||||
visible: false,
|
||||
ref: React.createRef(),
|
||||
},
|
||||
{
|
||||
id: "places",
|
||||
name: _t("Travel & Places"),
|
||||
name: _t("emoji|category_travel_places"),
|
||||
enabled: true,
|
||||
visible: false,
|
||||
ref: React.createRef(),
|
||||
},
|
||||
{
|
||||
id: "objects",
|
||||
name: _t("Objects"),
|
||||
name: _t("emoji|category_objects"),
|
||||
enabled: true,
|
||||
visible: false,
|
||||
ref: React.createRef(),
|
||||
},
|
||||
{
|
||||
id: "symbols",
|
||||
name: _t("Symbols"),
|
||||
name: _t("emoji|category_symbols"),
|
||||
enabled: true,
|
||||
visible: false,
|
||||
ref: React.createRef(),
|
||||
},
|
||||
{
|
||||
id: "flags",
|
||||
name: _t("Flags"),
|
||||
name: _t("emoji|category_flags"),
|
||||
enabled: true,
|
||||
visible: false,
|
||||
ref: React.createRef(),
|
||||
|
|
|
@ -95,7 +95,7 @@ class Header extends React.PureComponent<IProps> {
|
|||
<nav
|
||||
className="mx_EmojiPicker_header"
|
||||
role="tablist"
|
||||
aria-label={_t("Categories")}
|
||||
aria-label={_t("emoji|categories")}
|
||||
onKeyDown={this.onKeyDown}
|
||||
>
|
||||
{this.props.categories.map((category) => {
|
||||
|
|
|
@ -64,7 +64,7 @@ class QuickReactions extends React.Component<IProps, IState> {
|
|||
<section className="mx_EmojiPicker_footer mx_EmojiPicker_quick mx_EmojiPicker_category">
|
||||
<h2 className="mx_EmojiPicker_quick_header mx_EmojiPicker_category_label">
|
||||
{!this.state.hover ? (
|
||||
_t("Quick Reactions")
|
||||
_t("emoji|quick_reactions")
|
||||
) : (
|
||||
<React.Fragment>
|
||||
<span className="mx_EmojiPicker_name">{this.state.hover.label}</span>
|
||||
|
@ -72,7 +72,7 @@ class QuickReactions extends React.Component<IProps, IState> {
|
|||
</React.Fragment>
|
||||
)}
|
||||
</h2>
|
||||
<Toolbar className="mx_EmojiPicker_list" aria-label={_t("Quick Reactions")}>
|
||||
<Toolbar className="mx_EmojiPicker_list" aria-label={_t("emoji|quick_reactions")}>
|
||||
{QUICK_REACTIONS.map((emoji) => (
|
||||
<Emoji
|
||||
key={emoji.hexcode}
|
||||
|
|
|
@ -117,7 +117,7 @@ const EncryptionInfo: React.FC<IProps> = ({
|
|||
"For extra security, verify this user by checking a one-time code on both of your devices.",
|
||||
)}
|
||||
</p>
|
||||
<p>{_t("To be secure, do this in person or use a trusted way to communicate.")}</p>
|
||||
<p>{_t("encryption|verification|in_person")}</p>
|
||||
{content}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -90,14 +90,7 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
|
|||
const brand = SdkConfig.get().brand;
|
||||
|
||||
const noCommonMethodError: JSX.Element | null =
|
||||
!showSAS && !showQR ? (
|
||||
<p>
|
||||
{_t(
|
||||
"The device you are trying to verify doesn't support scanning a QR code or emoji verification, which is what %(brand)s supports. Try with a different client.",
|
||||
{ brand },
|
||||
)}
|
||||
</p>
|
||||
) : null;
|
||||
!showSAS && !showQR ? <p>{_t("encryption|verification|no_support_qr_emoji", { brand })}</p> : null;
|
||||
|
||||
if (this.props.layout === "dialog") {
|
||||
// HACK: This is a terrible idea.
|
||||
|
@ -106,7 +99,7 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
|
|||
if (showQR) {
|
||||
qrBlockDialog = (
|
||||
<div className="mx_VerificationPanel_QRPhase_startOption">
|
||||
<p>{_t("Scan this unique code")}</p>
|
||||
<p>{_t("encryption|verification|qr_prompt")}</p>
|
||||
<VerificationQRCode qrCodeBytes={this.state.qrCodeBytes} />
|
||||
</div>
|
||||
);
|
||||
|
@ -114,9 +107,9 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
|
|||
if (showSAS) {
|
||||
sasBlockDialog = (
|
||||
<div className="mx_VerificationPanel_QRPhase_startOption">
|
||||
<p>{_t("Compare unique emoji")}</p>
|
||||
<p>{_t("encryption|verification|sas_prompt")}</p>
|
||||
<span className="mx_VerificationPanel_QRPhase_helpText">
|
||||
{_t("Compare a unique set of emoji if you don't have a camera on either device")}
|
||||
{_t("encryption|verification|sas_description")}
|
||||
</span>
|
||||
<AccessibleButton
|
||||
disabled={this.state.emojiButtonClicked}
|
||||
|
@ -131,7 +124,7 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
|
|||
const or =
|
||||
qrBlockDialog && sasBlockDialog ? (
|
||||
<div className="mx_VerificationPanel_QRPhase_betweenText">
|
||||
{_t("%(qrCode)s or %(emojiCompare)s", {
|
||||
{_t("encryption|verification|qr_or_sas", {
|
||||
emojiCompare: "",
|
||||
qrCode: "",
|
||||
})}
|
||||
|
@ -139,7 +132,7 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
|
|||
) : null;
|
||||
return (
|
||||
<div>
|
||||
{_t("Verify this device by completing one of the following:")}
|
||||
{_t("encryption|verification|qr_or_sas_header")}
|
||||
<div className="mx_VerificationPanel_QRPhase_startOptions">
|
||||
{qrBlockDialog}
|
||||
{or}
|
||||
|
|
|
@ -457,7 +457,7 @@ export default class AliasSettings extends React.Component<IProps, IState> {
|
|||
>
|
||||
<details onToggle={this.onLocalAliasesToggled} open={this.state.detailsOpen}>
|
||||
<summary className="mx_AliasSettings_localAddresses">
|
||||
{this.state.detailsOpen ? _t("Show less") : _t("Show more")}
|
||||
{this.state.detailsOpen ? _t("room_list|show_less") : _t("Show more")}
|
||||
</summary>
|
||||
{localAliasesList}
|
||||
</details>
|
||||
|
|
|
@ -112,7 +112,7 @@ export const RoomKnocksBar: VFC<{ room: Room }> = ({ room }) => {
|
|||
</>
|
||||
);
|
||||
names = `${knockMembers[0].name} (${knockMembers[0].userId})`;
|
||||
link = (
|
||||
link = knockMembers[0].events.member?.getContent().reason && (
|
||||
<AccessibleButton
|
||||
className="mx_RoomKnocksBar_link"
|
||||
element="a"
|
||||
|
|
|
@ -157,11 +157,7 @@ const DmAuxButton: React.FC<IAuxButtonProps> = ({ tabIndex, dispatcher = default
|
|||
showSpaceInvite(activeSpace);
|
||||
}}
|
||||
disabled={!canInvite}
|
||||
tooltip={
|
||||
canInvite
|
||||
? undefined
|
||||
: _t("You do not have permissions to invite people to this space")
|
||||
}
|
||||
tooltip={canInvite ? undefined : _t("spaces|error_no_permission_invite")}
|
||||
/>
|
||||
)}
|
||||
</IconizedContextMenuOptionList>
|
||||
|
@ -253,11 +249,7 @@ const UntaggedAuxButton: React.FC<IAuxButtonProps> = ({ tabIndex }) => {
|
|||
PosthogTrackers.trackInteraction("WebRoomListRoomsSublistPlusMenuCreateRoomItem", e);
|
||||
}}
|
||||
disabled={!canAddRooms}
|
||||
tooltip={
|
||||
canAddRooms
|
||||
? undefined
|
||||
: _t("You do not have permissions to create new rooms in this space")
|
||||
}
|
||||
tooltip={canAddRooms ? undefined : _t("spaces|error_no_permission_create_room")}
|
||||
/>
|
||||
{videoRoomsEnabled && (
|
||||
<IconizedContextMenuOption
|
||||
|
@ -273,11 +265,7 @@ const UntaggedAuxButton: React.FC<IAuxButtonProps> = ({ tabIndex }) => {
|
|||
);
|
||||
}}
|
||||
disabled={!canAddRooms}
|
||||
tooltip={
|
||||
canAddRooms
|
||||
? undefined
|
||||
: _t("You do not have permissions to create new rooms in this space")
|
||||
}
|
||||
tooltip={canAddRooms ? undefined : _t("spaces|error_no_permission_create_room")}
|
||||
>
|
||||
<BetaPill />
|
||||
</IconizedContextMenuOption>
|
||||
|
@ -292,9 +280,7 @@ const UntaggedAuxButton: React.FC<IAuxButtonProps> = ({ tabIndex }) => {
|
|||
showAddExistingRooms(activeSpace);
|
||||
}}
|
||||
disabled={!canAddRooms}
|
||||
tooltip={
|
||||
canAddRooms ? undefined : _t("You do not have permissions to add rooms to this space")
|
||||
}
|
||||
tooltip={canAddRooms ? undefined : _t("spaces|error_no_permission_add_room")}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
|
|
|
@ -267,9 +267,7 @@ const RoomListHeader: React.FC<IProps> = ({ onVisibilityChange }) => {
|
|||
closePlusMenu();
|
||||
}}
|
||||
disabled={!canAddSubRooms}
|
||||
tooltip={
|
||||
!canAddSubRooms ? _t("You do not have permissions to add rooms to this space") : undefined
|
||||
}
|
||||
tooltip={!canAddSubRooms ? _t("spaces|error_no_permission_add_room") : undefined}
|
||||
/>
|
||||
{canCreateSpaces && (
|
||||
<IconizedContextMenuOption
|
||||
|
@ -282,11 +280,7 @@ const RoomListHeader: React.FC<IProps> = ({ onVisibilityChange }) => {
|
|||
closePlusMenu();
|
||||
}}
|
||||
disabled={!canAddSubSpaces}
|
||||
tooltip={
|
||||
!canAddSubSpaces
|
||||
? _t("You do not have permissions to add spaces to this space")
|
||||
: undefined
|
||||
}
|
||||
tooltip={!canAddSubSpaces ? _t("spaces|error_no_permission_add_space") : undefined}
|
||||
>
|
||||
<BetaPill />
|
||||
</IconizedContextMenuOption>
|
||||
|
|
|
@ -62,6 +62,7 @@ enum MessageCase {
|
|||
OtherError = "OtherError",
|
||||
PromptAskToJoin = "PromptAskToJoin",
|
||||
Knocked = "Knocked",
|
||||
RequestDenied = "requestDenied",
|
||||
}
|
||||
|
||||
interface IProps {
|
||||
|
@ -188,7 +189,11 @@ export default class RoomPreviewBar extends React.Component<IProps, IState> {
|
|||
const myMember = this.getMyMember();
|
||||
|
||||
if (myMember) {
|
||||
const previousMembership = myMember.events.member?.getPrevContent().membership;
|
||||
if (myMember.isKicked()) {
|
||||
if (previousMembership === "knock") {
|
||||
return MessageCase.RequestDenied;
|
||||
}
|
||||
return MessageCase.Kicked;
|
||||
} else if (myMember.membership === "ban") {
|
||||
return MessageCase.Banned;
|
||||
|
@ -397,6 +402,21 @@ export default class RoomPreviewBar extends React.Component<IProps, IState> {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case MessageCase.RequestDenied: {
|
||||
title = _t("You have been denied access");
|
||||
|
||||
subTitle = _t(
|
||||
"As you have been denied access, you cannot rejoin unless you are invited by the admin or moderator of the group.",
|
||||
);
|
||||
|
||||
if (isSpace) {
|
||||
primaryActionLabel = _t("Forget this space");
|
||||
} else {
|
||||
primaryActionLabel = _t("Forget this room");
|
||||
}
|
||||
primaryActionHandler = this.props.onForgetClick;
|
||||
break;
|
||||
}
|
||||
case MessageCase.Banned: {
|
||||
const { memberName, reason } = this.getKickOrBanInfo();
|
||||
if (roomName) {
|
||||
|
|
|
@ -584,14 +584,14 @@ export default class RoomSublist extends React.Component<IProps, IState> {
|
|||
onChange={this.onUnreadFirstChanged}
|
||||
checked={isUnreadFirst}
|
||||
>
|
||||
{_t("Show rooms with unread messages first")}
|
||||
{_t("room_list|sort_unread_first")}
|
||||
</StyledMenuItemCheckbox>
|
||||
<StyledMenuItemCheckbox
|
||||
onClose={this.onCloseMenu}
|
||||
onChange={this.onMessagePreviewChanged}
|
||||
checked={this.layout.showPreviews}
|
||||
>
|
||||
{_t("Show previews of messages")}
|
||||
{_t("room_list|show_previews")}
|
||||
</StyledMenuItemCheckbox>
|
||||
</fieldset>
|
||||
</React.Fragment>
|
||||
|
@ -607,14 +607,14 @@ export default class RoomSublist extends React.Component<IProps, IState> {
|
|||
>
|
||||
<div className="mx_RoomSublist_contextMenu">
|
||||
<fieldset>
|
||||
<legend className="mx_RoomSublist_contextMenu_title">{_t("Sort by")}</legend>
|
||||
<legend className="mx_RoomSublist_contextMenu_title">{_t("room_list|sort_by")}</legend>
|
||||
<StyledMenuItemRadio
|
||||
onClose={this.onCloseMenu}
|
||||
onChange={() => this.onTagSortChanged(SortAlgorithm.Recent)}
|
||||
checked={!isAlphabetical}
|
||||
name={`mx_${this.props.tagId}_sortBy`}
|
||||
>
|
||||
{_t("Activity")}
|
||||
{_t("room_list|sort_by_activity")}
|
||||
</StyledMenuItemRadio>
|
||||
<StyledMenuItemRadio
|
||||
onClose={this.onCloseMenu}
|
||||
|
@ -622,7 +622,7 @@ export default class RoomSublist extends React.Component<IProps, IState> {
|
|||
checked={isAlphabetical}
|
||||
name={`mx_${this.props.tagId}_sortBy`}
|
||||
>
|
||||
{_t("A-Z")}
|
||||
{_t("room_list|sort_by_alphabet")}
|
||||
</StyledMenuItemRadio>
|
||||
</fieldset>
|
||||
{otherSections}
|
||||
|
@ -636,7 +636,7 @@ export default class RoomSublist extends React.Component<IProps, IState> {
|
|||
<ContextMenuTooltipButton
|
||||
className="mx_RoomSublist_menuButton"
|
||||
onClick={this.onOpenMenuClick}
|
||||
title={_t("List options")}
|
||||
title={_t("room_list|sublist_options")}
|
||||
isExpanded={!!this.state.contextMenuPosition}
|
||||
/>
|
||||
{contextMenu}
|
||||
|
@ -788,7 +788,7 @@ export default class RoomSublist extends React.Component<IProps, IState> {
|
|||
if (this.slidingSyncMode) {
|
||||
numMissing = RoomListStore.instance.getCount(this.props.tagId) - amountFullyShown;
|
||||
}
|
||||
const label = _t("Show %(count)s more", { count: numMissing });
|
||||
const label = _t("room_list|show_n_more", { count: numMissing });
|
||||
let showMoreText: ReactNode = <span className="mx_RoomSublist_showNButtonText">{label}</span>;
|
||||
if (this.props.isMinimized) showMoreText = null;
|
||||
showNButton = (
|
||||
|
@ -806,7 +806,7 @@ export default class RoomSublist extends React.Component<IProps, IState> {
|
|||
);
|
||||
} else if (this.numTiles > this.layout.defaultVisibleTiles) {
|
||||
// we have all tiles visible - add a button to show less
|
||||
const label = _t("Show less");
|
||||
const label = _t("room_list|show_less");
|
||||
let showLessText: ReactNode = <span className="mx_RoomSublist_showNButtonText">{label}</span>;
|
||||
if (this.props.isMinimized) showLessText = null;
|
||||
showNButton = (
|
||||
|
|
|
@ -313,7 +313,7 @@ export class RoomTile extends React.PureComponent<ClassProps, State> {
|
|||
<ContextMenuTooltipButton
|
||||
className={classes}
|
||||
onClick={this.onNotificationsMenuOpenClick}
|
||||
title={_t("Notification options")}
|
||||
title={_t("room_list|notification_options")}
|
||||
isExpanded={!!this.state.notificationsMenuPosition}
|
||||
tabIndex={isActive ? 0 : -1}
|
||||
/>
|
||||
|
@ -433,17 +433,17 @@ export class RoomTile extends React.PureComponent<ClassProps, State> {
|
|||
} else if (this.notificationState.hasMentions) {
|
||||
ariaLabel +=
|
||||
" " +
|
||||
_t("%(count)s unread messages including mentions.", {
|
||||
_t("a11y|n_unread_messages_mentions", {
|
||||
count: this.notificationState.count,
|
||||
});
|
||||
} else if (this.notificationState.hasUnreadCount) {
|
||||
ariaLabel +=
|
||||
" " +
|
||||
_t("%(count)s unread messages.", {
|
||||
_t("a11y|n_unread_messages", {
|
||||
count: this.notificationState.count,
|
||||
});
|
||||
} else if (this.notificationState.isUnread) {
|
||||
ariaLabel += " " + _t("Unread messages.");
|
||||
ariaLabel += " " + _t("a11y|unread_messages");
|
||||
}
|
||||
|
||||
let ariaDescribedBy: string;
|
||||
|
|
|
@ -106,7 +106,11 @@ export default class FontScalingPanel extends React.Component<IProps, IState> {
|
|||
|
||||
public render(): React.ReactNode {
|
||||
return (
|
||||
<SettingsSubsection heading={_t("Font size")} stretchContent data-testid="mx_FontScalingPanel">
|
||||
<SettingsSubsection
|
||||
heading={_t("settings|appearance|font_size")}
|
||||
stretchContent
|
||||
data-testid="mx_FontScalingPanel"
|
||||
>
|
||||
<EventTilePreview
|
||||
className="mx_FontScalingPanel_preview"
|
||||
message={this.MESSAGE_PREVIEW_TEXT}
|
||||
|
@ -125,7 +129,7 @@ export default class FontScalingPanel extends React.Component<IProps, IState> {
|
|||
onChange={this.onFontSizeChanged}
|
||||
displayFunc={(_) => ""}
|
||||
disabled={this.state.useCustomFontSize}
|
||||
label={_t("Font size")}
|
||||
label={_t("settings|appearance|font_size")}
|
||||
/>
|
||||
<div className="mx_FontScalingPanel_fontSlider_largeText">Aa</div>
|
||||
</div>
|
||||
|
@ -148,7 +152,7 @@ export default class FontScalingPanel extends React.Component<IProps, IState> {
|
|||
|
||||
<Field
|
||||
type="number"
|
||||
label={_t("Font size")}
|
||||
label={_t("settings|appearance|font_size")}
|
||||
autoComplete="off"
|
||||
placeholder={this.state.fontSize.toString()}
|
||||
value={this.state.fontSize.toString()}
|
||||
|
|
|
@ -50,7 +50,7 @@ export default class ImageSizePanel extends React.Component<IProps, IState> {
|
|||
|
||||
public render(): React.ReactNode {
|
||||
return (
|
||||
<SettingsSubsection heading={_t("Image size in the timeline")}>
|
||||
<SettingsSubsection heading={_t("settings|appearance|timeline_image_size")}>
|
||||
<div className="mx_ImageSizePanel_radios">
|
||||
<label>
|
||||
<div className="mx_ImageSizePanel_size mx_ImageSizePanel_sizeDefault" />
|
||||
|
|
|
@ -104,6 +104,6 @@ export default class IntegrationManager extends React.Component<IProps, IState>
|
|||
);
|
||||
}
|
||||
|
||||
return <iframe title={_t("Integration manager")} src={this.props.url} onError={this.onError} />;
|
||||
return <iframe title={_t("common|integration_manager")} src={this.props.url} onError={this.onError} />;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,8 +14,15 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { ReactNode } from "react";
|
||||
import { IJoinRuleEventContent, JoinRule, RestrictedAllowType, Room, EventType } from "matrix-js-sdk/src/matrix";
|
||||
import React, { ReactNode, useEffect, useState } from "react";
|
||||
import {
|
||||
IJoinRuleEventContent,
|
||||
JoinRule,
|
||||
RestrictedAllowType,
|
||||
Room,
|
||||
EventType,
|
||||
Visibility,
|
||||
} from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import StyledRadioGroup, { IDefinition } from "../elements/StyledRadioGroup";
|
||||
import { _t } from "../../../languageHandler";
|
||||
|
@ -34,6 +41,7 @@ import { Action } from "../../../dispatcher/actions";
|
|||
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
|
||||
import { doesRoomVersionSupport, PreferredRoomVersions } from "../../../utils/PreferredRoomVersions";
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import LabelledCheckbox from "../elements/LabelledCheckbox";
|
||||
|
||||
export interface JoinRuleSettingsProps {
|
||||
room: Room;
|
||||
|
@ -76,6 +84,22 @@ const JoinRuleSettings: React.FC<JoinRuleSettingsProps> = ({
|
|||
? content?.allow?.filter((o) => o.type === RestrictedAllowType.RoomMembership).map((o) => o.room_id)
|
||||
: undefined;
|
||||
|
||||
const [isPublicKnockRoom, setIsPublicKnockRoom] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (joinRule === JoinRule.Knock) {
|
||||
cli.getRoomDirectoryVisibility(room.roomId)
|
||||
.then(({ visibility }) => setIsPublicKnockRoom(visibility === Visibility.Public))
|
||||
.catch(onError);
|
||||
}
|
||||
}, [cli, joinRule, onError, room.roomId]);
|
||||
|
||||
const onIsPublicKnockRoomChange = (checked: boolean): void => {
|
||||
cli.setRoomDirectoryVisibility(room.roomId, checked ? Visibility.Public : Visibility.Private)
|
||||
.then(() => setIsPublicKnockRoom(checked))
|
||||
.catch(onError);
|
||||
};
|
||||
|
||||
const editRestrictedRoomIds = async (): Promise<string[] | undefined> => {
|
||||
let selected = restrictedAllowRoomIds;
|
||||
if (!selected?.length && SpaceStore.instance.activeSpaceRoom) {
|
||||
|
@ -297,7 +321,22 @@ const JoinRuleSettings: React.FC<JoinRuleSettingsProps> = ({
|
|||
{preferredKnockVersion && upgradeRequiredPill}
|
||||
</>
|
||||
),
|
||||
description: _t("People cannot join unless access is granted."),
|
||||
description: (
|
||||
<>
|
||||
{_t("People cannot join unless access is granted.")}
|
||||
<LabelledCheckbox
|
||||
className="mx_JoinRuleSettings_labelledCheckbox"
|
||||
disabled={joinRule !== JoinRule.Knock}
|
||||
label={
|
||||
room.isSpaceRoom()
|
||||
? _t("Make this space visible in the public room directory.")
|
||||
: _t("Make this room visible in the public room directory.")
|
||||
}
|
||||
onChange={onIsPublicKnockRoomChange}
|
||||
value={isPublicKnockRoom}
|
||||
/>
|
||||
</>
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -85,7 +85,7 @@ export default class LayoutSwitcher extends React.Component<IProps, IState> {
|
|||
checked={this.state.layout === Layout.IRC}
|
||||
onChange={this.onLayoutChange}
|
||||
>
|
||||
{_t("IRC (Experimental)")}
|
||||
{_t("settings|appearance|layout_irc")}
|
||||
</StyledRadioButton>
|
||||
</label>
|
||||
<label className={groupClasses}>
|
||||
|
@ -121,7 +121,7 @@ export default class LayoutSwitcher extends React.Component<IProps, IState> {
|
|||
checked={this.state.layout == Layout.Bubble}
|
||||
onChange={this.onLayoutChange}
|
||||
>
|
||||
{_t("Message bubbles")}
|
||||
{_t("settings|appearance|layout_bubbles")}
|
||||
</StyledRadioButton>
|
||||
</label>
|
||||
</div>
|
||||
|
|
|
@ -381,7 +381,7 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
|
|||
if (category === KEYWORD_RULE_CATEGORY) {
|
||||
preparedNewState.vectorPushRules[category]!.push({
|
||||
ruleId: KEYWORD_RULE_ID,
|
||||
description: _t("Messages containing keywords"),
|
||||
description: _t("settings|notifications|messages_containing_keywords"),
|
||||
vectorState: preparedNewState.vectorKeywordRuleInfo.vectorState,
|
||||
});
|
||||
}
|
||||
|
@ -400,8 +400,8 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
|
|||
|
||||
private showSaveError(): void {
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: _t("Error saving notification preferences"),
|
||||
description: _t("An error occurred whilst saving your notification preferences."),
|
||||
title: _t("settings|notifications|error_saving"),
|
||||
description: _t("settings|notifications|error_saving_detail"),
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -661,8 +661,8 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
|
|||
<LabelledToggleSwitch
|
||||
data-testid="notif-master-switch"
|
||||
value={!this.isInhibited}
|
||||
label={_t("Enable notifications for this account")}
|
||||
caption={_t("Turn off to disable notifications on all your devices and sessions")}
|
||||
label={_t("settings|notifications|enable_notifications_account")}
|
||||
caption={_t("settings|notifications|enable_notifications_account_detail")}
|
||||
onChange={this.onMasterRuleChanged}
|
||||
disabled={this.state.phase === Phase.Persisting}
|
||||
/>
|
||||
|
@ -680,7 +680,7 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
|
|||
data-testid="notif-email-switch"
|
||||
key={e.address}
|
||||
value={!!this.state.pushers?.some((p) => p.kind === "email" && p.pushkey === e.address)}
|
||||
label={_t("Enable email notifications for %(email)s", { email: e.address })}
|
||||
label={_t("settings|notifications|enable_email_notifications", { email: e.address })}
|
||||
onChange={this.onEmailNotificationsChanged.bind(this, e.address)}
|
||||
disabled={this.state.phase === Phase.Persisting}
|
||||
/>
|
||||
|
@ -693,7 +693,7 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
|
|||
<LabelledToggleSwitch
|
||||
data-testid="notif-device-switch"
|
||||
value={this.state.deviceNotificationsEnabled}
|
||||
label={_t("Enable notifications for this device")}
|
||||
label={_t("settings|notifications|enable_notifications_device")}
|
||||
onChange={(checked) => this.updateDeviceNotifications(checked)}
|
||||
disabled={this.state.phase === Phase.Persisting}
|
||||
/>
|
||||
|
@ -704,21 +704,21 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
|
|||
data-testid="notif-setting-notificationsEnabled"
|
||||
value={this.state.desktopNotifications}
|
||||
onChange={this.onDesktopNotificationsChanged}
|
||||
label={_t("Enable desktop notifications for this session")}
|
||||
label={_t("settings|notifications|enable_desktop_notifications_session")}
|
||||
disabled={this.state.phase === Phase.Persisting}
|
||||
/>
|
||||
<LabelledToggleSwitch
|
||||
data-testid="notif-setting-notificationBodyEnabled"
|
||||
value={this.state.desktopShowBody}
|
||||
onChange={this.onDesktopShowBodyChanged}
|
||||
label={_t("Show message in desktop notification")}
|
||||
label={_t("settings|notifications|show_message_desktop_notification")}
|
||||
disabled={this.state.phase === Phase.Persisting}
|
||||
/>
|
||||
<LabelledToggleSwitch
|
||||
data-testid="notif-setting-audioNotificationsEnabled"
|
||||
value={this.state.audioNotifications}
|
||||
onChange={this.onAudioNotificationsChanged}
|
||||
label={_t("Enable audible notifications for this session")}
|
||||
label={_t("settings|notifications|enable_audible_notifications_session")}
|
||||
disabled={this.state.phase === Phase.Persisting}
|
||||
/>
|
||||
</>
|
||||
|
|
|
@ -396,7 +396,7 @@ export default class SetIdServer extends React.Component<IProps, IState> {
|
|||
);
|
||||
}
|
||||
} else {
|
||||
sectionTitle = _t("Identity server");
|
||||
sectionTitle = _t("common|identity_server");
|
||||
bodyText = _t(
|
||||
"You are not currently using an identity server. To discover and be discoverable by existing contacts you know, add one below.",
|
||||
);
|
||||
|
|
|
@ -141,18 +141,25 @@ export default class ThemeChoicePanel extends React.Component<IProps, IState> {
|
|||
// XXX: need some schema for this
|
||||
const themeInfo = await r.json();
|
||||
if (!themeInfo || typeof themeInfo["name"] !== "string" || typeof themeInfo["colors"] !== "object") {
|
||||
this.setState({ customThemeMessage: { text: _t("Invalid theme schema."), isError: true } });
|
||||
this.setState({
|
||||
customThemeMessage: { text: _t("settings|appearance|custom_theme_invalid"), isError: true },
|
||||
});
|
||||
return;
|
||||
}
|
||||
currentThemes.push(themeInfo);
|
||||
} catch (e) {
|
||||
logger.error(e);
|
||||
this.setState({ customThemeMessage: { text: _t("Error downloading theme information."), isError: true } });
|
||||
this.setState({
|
||||
customThemeMessage: { text: _t("settings|appearance|custom_theme_error_downloading"), isError: true },
|
||||
});
|
||||
return; // Don't continue on error
|
||||
}
|
||||
|
||||
await SettingsStore.setValue("custom_themes", null, SettingLevel.ACCOUNT, currentThemes);
|
||||
this.setState({ customThemeUrl: "", customThemeMessage: { text: _t("Theme added!"), isError: false } });
|
||||
this.setState({
|
||||
customThemeUrl: "",
|
||||
customThemeMessage: { text: _t("settings|appearance|custom_theme_success"), isError: false },
|
||||
});
|
||||
|
||||
this.themeTimer = window.setTimeout(() => {
|
||||
this.setState({ customThemeMessage: { text: "", isError: false } });
|
||||
|
@ -174,7 +181,7 @@ export default class ThemeChoicePanel extends React.Component<IProps, IState> {
|
|||
checked={isHighContrastTheme(this.state.theme)}
|
||||
onChange={(e) => this.highContrastThemeChanged(e.target.checked)}
|
||||
>
|
||||
{_t("Use high contrast")}
|
||||
{_t("settings|appearance|use_high_contrast")}
|
||||
</StyledCheckbox>
|
||||
</div>
|
||||
);
|
||||
|
@ -223,7 +230,7 @@ export default class ThemeChoicePanel extends React.Component<IProps, IState> {
|
|||
<div className="mx_SettingsTab_section">
|
||||
<form onSubmit={this.onAddCustomTheme}>
|
||||
<Field
|
||||
label={_t("Custom theme URL")}
|
||||
label={_t("settings|appearance|custom_theme_url")}
|
||||
type="text"
|
||||
id="mx_GeneralUserSettingsTab_customThemeInput"
|
||||
autoComplete="off"
|
||||
|
@ -236,7 +243,7 @@ export default class ThemeChoicePanel extends React.Component<IProps, IState> {
|
|||
kind="primary_sm"
|
||||
disabled={!this.state.customThemeUrl.trim()}
|
||||
>
|
||||
{_t("Add theme")}
|
||||
{_t("settings|appearance|custom_theme_add_button")}
|
||||
</AccessibleButton>
|
||||
{messageElement}
|
||||
</form>
|
||||
|
|
|
@ -56,7 +56,7 @@ export const deleteDevicesWithInteractiveAuth = async (
|
|||
body: _t("Confirm logging out these devices by using Single Sign On to prove your identity.", {
|
||||
count: numDevices,
|
||||
}),
|
||||
continueText: _t("Single Sign On"),
|
||||
continueText: _t("auth|sso"),
|
||||
continueKind: "primary",
|
||||
},
|
||||
[SSOAuthEntry.PHASE_POSTAUTH]: {
|
||||
|
|
|
@ -122,7 +122,7 @@ export default function NotificationSettings2(): JSX.Element {
|
|||
<SettingsSection heading={_t("Notifications")}>
|
||||
<div className="mx_SettingsSubsection_content mx_NotificationSettings2_flags">
|
||||
<LabelledToggleSwitch
|
||||
label={_t("Enable notifications for this account")}
|
||||
label={_t("settings|notifications|enable_notifications_account")}
|
||||
value={!settings.globalMute}
|
||||
disabled={disabled}
|
||||
onChange={(value) => {
|
||||
|
@ -133,7 +133,7 @@ export default function NotificationSettings2(): JSX.Element {
|
|||
}}
|
||||
/>
|
||||
<LabelledToggleSwitch
|
||||
label={_t("Enable desktop notifications for this session")}
|
||||
label={_t("settings|notifications|enable_desktop_notifications_session")}
|
||||
value={desktopNotifications}
|
||||
onChange={(value) =>
|
||||
SettingsStore.setValue("notificationsEnabled", null, SettingLevel.DEVICE, value)
|
||||
|
@ -147,7 +147,7 @@ export default function NotificationSettings2(): JSX.Element {
|
|||
}
|
||||
/>
|
||||
<LabelledToggleSwitch
|
||||
label={_t("Enable audible notifications for this session")}
|
||||
label={_t("settings|notifications|enable_audible_notifications_session")}
|
||||
value={audioNotifications}
|
||||
onChange={(value) =>
|
||||
SettingsStore.setValue("audioNotificationsEnabled", null, SettingLevel.DEVICE, value)
|
||||
|
|
|
@ -106,10 +106,7 @@ export default class AppearanceUserSettingsTab extends React.Component<IProps, I
|
|||
let advanced: React.ReactNode;
|
||||
|
||||
if (this.state.showAdvanced) {
|
||||
const tooltipContent = _t(
|
||||
"Set the name of a font installed on your system & %(brand)s will attempt to use it.",
|
||||
{ brand },
|
||||
);
|
||||
const tooltipContent = _t("settings|appearance|custom_font_description", { brand });
|
||||
advanced = (
|
||||
<>
|
||||
<SettingsFlag name="useCompactLayout" level={SettingLevel.DEVICE} useCheckbox={true} />
|
||||
|
@ -151,10 +148,8 @@ export default class AppearanceUserSettingsTab extends React.Component<IProps, I
|
|||
|
||||
return (
|
||||
<SettingsTab data-testid="mx_AppearanceUserSettingsTab">
|
||||
<SettingsSection heading={_t("Customise your appearance")}>
|
||||
<SettingsSubsectionText>
|
||||
{_t("Appearance Settings only affect this %(brand)s session.", { brand })}
|
||||
</SettingsSubsectionText>
|
||||
<SettingsSection heading={_t("settings|appearance|heading")}>
|
||||
<SettingsSubsectionText>{_t("settings|appearance|subheading", { brand })}</SettingsSubsectionText>
|
||||
<ThemeChoicePanel />
|
||||
<LayoutSwitcher
|
||||
userId={this.state.userId}
|
||||
|
|
|
@ -78,8 +78,8 @@ export default class HelpUserSettingsTab extends React.Component<IProps, IState>
|
|||
: "<not-enabled>";
|
||||
|
||||
return {
|
||||
appVersion: `${_t("%(brand)s version:", { brand })} ${appVersion}`,
|
||||
olmVersion: `${_t("Olm version:")} ${olmVersion}`,
|
||||
appVersion: `${_t("setting|help_about|brand_version", { brand })} ${appVersion}`,
|
||||
olmVersion: `${_t("setting|help_about|olm_version")} ${olmVersion}`,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -228,7 +228,7 @@ export default class HelpUserSettingsTab extends React.Component<IProps, IState>
|
|||
const brand = SdkConfig.get().brand;
|
||||
|
||||
let faqText = _t(
|
||||
"For help with using %(brand)s, click <a>here</a>.",
|
||||
"setting|help_about|help_link",
|
||||
{
|
||||
brand,
|
||||
},
|
||||
|
@ -240,7 +240,7 @@ export default class HelpUserSettingsTab extends React.Component<IProps, IState>
|
|||
faqText = (
|
||||
<div>
|
||||
{_t(
|
||||
"For help with using %(brand)s, click <a>here</a> or start a chat with our bot using the button below.",
|
||||
"setting|help_about|help_link_chat_bot",
|
||||
{
|
||||
brand,
|
||||
},
|
||||
|
@ -258,7 +258,7 @@ export default class HelpUserSettingsTab extends React.Component<IProps, IState>
|
|||
)}
|
||||
<div>
|
||||
<AccessibleButton onClick={this.onStartBotChat} kind="primary">
|
||||
{_t("Chat with %(brand)s Bot", { brand })}
|
||||
{_t("setting|help_about|chat_bot", { brand })}
|
||||
</AccessibleButton>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -306,10 +306,10 @@ export default class HelpUserSettingsTab extends React.Component<IProps, IState>
|
|||
|
||||
return (
|
||||
<SettingsTab>
|
||||
<SettingsSection heading={_t("Help & About")}>
|
||||
<SettingsSection heading={_t("setting|help_about|title")}>
|
||||
{bugReportingSection}
|
||||
<SettingsSubsection heading={_t("common|faq")} description={faqText} />
|
||||
<SettingsSubsection heading={_t("Versions")}>
|
||||
<SettingsSubsection heading={_t("setting|help_about|versions")}>
|
||||
<SettingsSubsectionText>
|
||||
<CopyableText getTextToCopy={this.getVersionTextToCopy}>
|
||||
{appVersion}
|
||||
|
@ -325,7 +325,7 @@ export default class HelpUserSettingsTab extends React.Component<IProps, IState>
|
|||
<SettingsSubsection heading={_t("Advanced")}>
|
||||
<SettingsSubsectionText>
|
||||
{_t(
|
||||
"Homeserver is <code>%(homeserverUrl)s</code>",
|
||||
"setting|help_about|homeserver",
|
||||
{
|
||||
homeserverUrl: this.context.getHomeserverUrl(),
|
||||
},
|
||||
|
@ -337,7 +337,7 @@ export default class HelpUserSettingsTab extends React.Component<IProps, IState>
|
|||
{this.context.getIdentityServerUrl() && (
|
||||
<SettingsSubsectionText>
|
||||
{_t(
|
||||
"Identity server is <code>%(identityServerUrl)s</code>",
|
||||
"setting|help_about|identity_server",
|
||||
{
|
||||
identityServerUrl: this.context.getIdentityServerUrl(),
|
||||
},
|
||||
|
@ -350,18 +350,14 @@ export default class HelpUserSettingsTab extends React.Component<IProps, IState>
|
|||
<SettingsSubsectionText>
|
||||
<details>
|
||||
<summary>{_t("common|access_token")}</summary>
|
||||
<b>
|
||||
{_t(
|
||||
"Your access token gives full access to your account. Do not share it with anyone.",
|
||||
)}
|
||||
</b>
|
||||
<b>{_t("setting|help_about|access_token_detail")}</b>
|
||||
<CopyableText getTextToCopy={() => this.context.getAccessToken()}>
|
||||
{this.context.getAccessToken()}
|
||||
</CopyableText>
|
||||
</details>
|
||||
</SettingsSubsectionText>
|
||||
<AccessibleButton onClick={this.onClearCacheAndReload} kind="danger">
|
||||
{_t("Clear cache and reload")}
|
||||
{_t("setting|help_about|clear_cache_reload")}
|
||||
</AccessibleButton>
|
||||
</SettingsSubsection>
|
||||
</SettingsSection>
|
||||
|
|
|
@ -27,7 +27,7 @@ export default class VerificationCancelled extends React.Component<IProps> {
|
|||
public render(): React.ReactNode {
|
||||
return (
|
||||
<div>
|
||||
<p>{_t("The other party cancelled the verification.")}</p>
|
||||
<p>{_t("encryption|verification|other_party_cancelled")}</p>
|
||||
<DialogButtons
|
||||
primaryButton={_t("action|ok")}
|
||||
hasCancel={false}
|
||||
|
|
|
@ -27,8 +27,8 @@ export default class VerificationComplete extends React.Component<IProps> {
|
|||
public render(): React.ReactNode {
|
||||
return (
|
||||
<div>
|
||||
<h2>{_t("Verified!")}</h2>
|
||||
<p>{_t("You've successfully verified this user.")}</p>
|
||||
<h2>{_t("encryption|verification|complete_title")}</h2>
|
||||
<p>{_t("encryption|verification|complete_description")}</p>
|
||||
<p>
|
||||
{_t(
|
||||
"Secure messages with this user are end-to-end encrypted and not able to be read by third parties.",
|
||||
|
|
|
@ -180,10 +180,10 @@ export default class VerificationShowSas extends React.Component<IProps, IState>
|
|||
confirm = (
|
||||
<div className="mx_VerificationShowSas_buttonRow">
|
||||
<AccessibleButton onClick={this.onDontMatchClick} kind="danger">
|
||||
{_t("They don't match")}
|
||||
{_t("encryption|verification|sas_no_match")}
|
||||
</AccessibleButton>
|
||||
<AccessibleButton onClick={this.onMatchClick} kind="primary">
|
||||
{_t("They match")}
|
||||
{_t("encryption|verification|sas_match")}
|
||||
</AccessibleButton>
|
||||
</div>
|
||||
);
|
||||
|
@ -193,11 +193,7 @@ export default class VerificationShowSas extends React.Component<IProps, IState>
|
|||
<div className="mx_VerificationShowSas">
|
||||
<p>{sasCaption}</p>
|
||||
{sasDisplay}
|
||||
<p>
|
||||
{this.props.isSelf
|
||||
? ""
|
||||
: _t("To be secure, do this in person or use a trusted way to communicate.")}
|
||||
</p>
|
||||
<p>{this.props.isSelf ? "" : _t("encryption|verification|in_person")}</p>
|
||||
{confirm}
|
||||
</div>
|
||||
);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue