* Split translations between EW and shared components Uses update module API with global TranslationKey type that can be overridden. WIP. * Removed the wrong script (for now) * Add the type files * Add shared components i18n file * More i18n strings * Add i18n check for shared conmponents * Needs a different name * rerun i18n for merge from develop, fix test * Move translated strings to shared components file NB. there are lots of removed strings for a few languages where we seem to have hit a localazy bug or something where the key/value for plurals got switched, making the translations invalid. They've been missing for a while so I'm removing them rather than trying to restore them, * Add shared components files to localazy * Merge element web & shared component translations for the built app * Use right translations for shared component tests and fix missign en_EN strings * Pull shared components translations too * Fix/disable warnings * We can now remove the build:res call ...right? (right?) * Remove webpack import for languages index ..and just load it using a relative path which we do for the individual language files and also did anyway for the index because even in non-test it was an object, not a string, so we always usesd the 'test' code path. * Make the storybook language selector work ...without referring to the parent app's files * Revert unnecessary yarn lock change * Typo Co-authored-by: Michael Telatynski <7t3chguy@gmail.com> * Add comment on why we use merge * Fix localazy download config to actually put the translations in the right place * Better typescript syntax Co-authored-by: Michael Telatynski <7t3chguy@gmail.com> * Watch both translations files --------- Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
221 lines
7.3 KiB
TypeScript
221 lines
7.3 KiB
TypeScript
/*
|
|
Copyright 2024 New Vector Ltd.
|
|
Copyright 2018-2021 The Matrix.org Foundation C.I.C.
|
|
|
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
|
Please see LICENSE files in the repository root for full details.
|
|
*/
|
|
|
|
import React, { type ReactNode } from "react";
|
|
import { MatrixError, ConnectionError } from "matrix-js-sdk/src/matrix";
|
|
import { logger } from "matrix-js-sdk/src/logger";
|
|
|
|
import { _t, _td, lookupString, type Tags, type TranslatedString } from "../languageHandler";
|
|
import SdkConfig from "../SdkConfig";
|
|
import { type ValidatedServerConfig } from "./ValidatedServerConfig";
|
|
import ExternalLink from "../components/views/elements/ExternalLink";
|
|
import Modal from "../Modal.tsx";
|
|
import ErrorDialog from "../components/views/dialogs/ErrorDialog.tsx";
|
|
|
|
export const resourceLimitStrings = {
|
|
"monthly_active_user": _td("error|mau"),
|
|
"hs_blocked": _td("error|hs_blocked"),
|
|
"": _td("error|resource_limits"),
|
|
};
|
|
|
|
export const adminContactStrings = {
|
|
"": _td("error|admin_contact"),
|
|
};
|
|
|
|
/**
|
|
* Produce a translated error message for a
|
|
* M_RESOURCE_LIMIT_EXCEEDED error
|
|
*
|
|
* @param {string} limitType The limit_type from the error
|
|
* @param {string} adminContact The admin_contact from the error
|
|
* @param {Object} strings Translatable string for different
|
|
* limit_type. Must include at least the empty string key
|
|
* which is the default. Strings may include an 'a' tag
|
|
* for the admin contact link.
|
|
* @param {Object} extraTranslations Extra translation substitution functions
|
|
* for any tags in the strings apart from 'a'
|
|
* @returns {*} Translated string or react component
|
|
*/
|
|
export function messageForResourceLimitError(
|
|
limitType: string | undefined,
|
|
adminContact: string | undefined,
|
|
strings: Record<string, TranslationKey>,
|
|
extraTranslations?: Tags,
|
|
): TranslatedString {
|
|
let errString = limitType ? strings[limitType] : undefined;
|
|
if (errString === undefined) errString = strings[""];
|
|
|
|
const linkSub = (sub: string): ReactNode => {
|
|
if (adminContact) {
|
|
return (
|
|
<a href={adminContact} target="_blank" rel="noreferrer noopener">
|
|
{sub}
|
|
</a>
|
|
);
|
|
} else {
|
|
return sub;
|
|
}
|
|
};
|
|
|
|
if (lookupString(errString).includes("<a>")) {
|
|
return _t(errString, {}, Object.assign({ a: linkSub }, extraTranslations));
|
|
} else {
|
|
return _t(errString, {}, extraTranslations!);
|
|
}
|
|
}
|
|
|
|
export function messageForSyncError(err: Error): ReactNode {
|
|
if (err instanceof MatrixError && err.errcode === "M_RESOURCE_LIMIT_EXCEEDED") {
|
|
const limitError = messageForResourceLimitError(
|
|
err.data.limit_type,
|
|
err.data.admin_contact,
|
|
resourceLimitStrings,
|
|
);
|
|
const adminContact = messageForResourceLimitError(
|
|
err.data.limit_type,
|
|
err.data.admin_contact,
|
|
adminContactStrings,
|
|
);
|
|
return (
|
|
<div>
|
|
<div>{limitError}</div>
|
|
<div>{adminContact}</div>
|
|
</div>
|
|
);
|
|
} else {
|
|
return <div>{_t("error|sync")}</div>;
|
|
}
|
|
}
|
|
|
|
export function messageForLoginError(
|
|
err: MatrixError,
|
|
serverConfig: Pick<ValidatedServerConfig, "hsName" | "hsUrl">,
|
|
): ReactNode {
|
|
if (err.errcode === "M_RESOURCE_LIMIT_EXCEEDED") {
|
|
const errorTop = messageForResourceLimitError(
|
|
err.data.limit_type,
|
|
err.data.admin_contact,
|
|
resourceLimitStrings,
|
|
);
|
|
const errorDetail = messageForResourceLimitError(
|
|
err.data.limit_type,
|
|
err.data.admin_contact,
|
|
adminContactStrings,
|
|
);
|
|
return (
|
|
<div>
|
|
<div>{errorTop}</div>
|
|
<div className="mx_Login_smallError">{errorDetail}</div>
|
|
</div>
|
|
);
|
|
} else if (err.httpStatus === 401 || err.httpStatus === 403) {
|
|
if (err.errcode === "M_USER_DEACTIVATED") {
|
|
return _t("auth|account_deactivated");
|
|
} else if (SdkConfig.get("disable_custom_urls")) {
|
|
return (
|
|
<div>
|
|
<div>{_t("auth|incorrect_credentials")}</div>
|
|
<div className="mx_Login_smallError">
|
|
{_t("auth|incorrect_credentials_detail", {
|
|
hs: serverConfig.hsName,
|
|
})}
|
|
</div>
|
|
</div>
|
|
);
|
|
} else {
|
|
return _t("auth|incorrect_credentials");
|
|
}
|
|
} else {
|
|
return messageForConnectionError(err, serverConfig);
|
|
}
|
|
}
|
|
|
|
export function messageForConnectionError(
|
|
err: Error,
|
|
serverConfig: Pick<ValidatedServerConfig, "hsName" | "hsUrl">,
|
|
): ReactNode {
|
|
let errorText = _t("error|connection");
|
|
|
|
if (err instanceof ConnectionError) {
|
|
if (
|
|
window.location.protocol === "https:" &&
|
|
(serverConfig.hsUrl.startsWith("http:") || !serverConfig.hsUrl.startsWith("http"))
|
|
) {
|
|
return (
|
|
<span>
|
|
{_t(
|
|
"error|mixed_content",
|
|
{},
|
|
{
|
|
a: (sub) => {
|
|
return (
|
|
<a
|
|
target="_blank"
|
|
rel="noreferrer noopener"
|
|
href="https://www.google.com/search?&q=enable%20unsafe%20scripts"
|
|
>
|
|
{sub}
|
|
</a>
|
|
);
|
|
},
|
|
},
|
|
)}
|
|
</span>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<span>
|
|
{_t(
|
|
"error|tls",
|
|
{},
|
|
{
|
|
a: (sub) => (
|
|
<ExternalLink target="_blank" rel="noreferrer noopener" href={serverConfig.hsUrl}>
|
|
{sub}
|
|
</ExternalLink>
|
|
),
|
|
},
|
|
)}
|
|
</span>
|
|
);
|
|
} else if (err instanceof MatrixError) {
|
|
if (err.errcode) {
|
|
errorText += `(${err.errcode})`;
|
|
} else if (err.httpStatus) {
|
|
errorText += ` (HTTP ${err.httpStatus})`;
|
|
}
|
|
}
|
|
|
|
return errorText;
|
|
}
|
|
|
|
/**
|
|
* Utility for handling unexpected errors: pops up the error dialog.
|
|
*
|
|
* Example usage:
|
|
* ```
|
|
* try {
|
|
* /// complicated operation
|
|
* } catch (e) {
|
|
* logErrorAndShowErrorDialog("Failed complicated operation", e);
|
|
* }
|
|
* ```
|
|
*
|
|
* This isn't particularly intended to be pretty; rather it lets the user know that *something* has gone wrong so that
|
|
* they can report a bug. The general idea is that it's better to let the user know of a failure, even if they
|
|
* can't do anything about it, than it is to fail silently with the appearance of success.
|
|
*
|
|
* @param title - Title for the error dialog.
|
|
* @param error - The thrown error. Becomes the content of the error dialog.
|
|
*/
|
|
export function logErrorAndShowErrorDialog(title: string, error: any): void {
|
|
logger.error(`${title}:`, error);
|
|
Modal.createDialog(ErrorDialog, { title, description: `${error}` });
|
|
}
|