diff --git a/.eslintrc.js b/.eslintrc.cjs
similarity index 100%
rename from .eslintrc.js
rename to .eslintrc.cjs
diff --git a/i18next-parser.config.js b/i18next-parser.config.js
new file mode 100644
index 00000000..b2d58428
--- /dev/null
+++ b/i18next-parser.config.js
@@ -0,0 +1,20 @@
+export default {
+ keySeparator: false,
+ namespaceSeparator: false,
+ contextSeparator: "|",
+ pluralSeparator: "|",
+ createOldCatalogs: false,
+ defaultNamespace: "app",
+ lexers: {
+ ts: [{
+ lexer: "JavascriptLexer",
+ functions: ["t", "translatedError"],
+ functionsNamespace: ["useTranslation", "withTranslation"],
+ }],
+ },
+ locales: ["en-GB"],
+ output: "public/locales/$LOCALE/$NAMESPACE.json",
+ input: ["src/**/*.{ts,tsx}"],
+ sort: true,
+ useKeysAsDefaultValue: true,
+};
diff --git a/package.json b/package.json
index 7cda11b2..ab778355 100644
--- a/package.json
+++ b/package.json
@@ -1,5 +1,6 @@
{
"version": "0.0.0",
+ "type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
@@ -10,7 +11,8 @@
"prettier:format": "prettier -w src",
"lint": "yarn lint:types && yarn lint:js",
"lint:js": "eslint --max-warnings 0 src",
- "lint:types": "tsc"
+ "lint:types": "tsc",
+ "i18n": "node_modules/i18next-parser/bin/cli.js"
},
"dependencies": {
"@juggle/resize-observer": "^3.3.1",
@@ -38,6 +40,9 @@
"classnames": "^2.3.1",
"color-hash": "^2.0.1",
"events": "^3.3.0",
+ "i18next": "^21.10.0",
+ "i18next-browser-languagedetector": "^6.1.8",
+ "i18next-http-backend": "^1.4.4",
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#ce3b72c85031f188a092d1c39806ef7536e65bdd",
"matrix-widget-api": "^1.0.0",
"mermaid": "^8.13.8",
@@ -47,6 +52,7 @@
"re-resizable": "^6.9.0",
"react": "^17.0.0",
"react-dom": "^17.0.0",
+ "react-i18next": "^11.18.6",
"react-json-view": "^1.21.3",
"react-router": "6",
"react-router-dom": "^5.2.0",
@@ -71,6 +77,7 @@
"eslint-plugin-matrix-org": "^0.4.0",
"eslint-plugin-react": "^7.29.4",
"eslint-plugin-react-hooks": "^4.5.0",
+ "i18next-parser": "^6.6.0",
"prettier": "^2.6.2",
"sass": "^1.42.1",
"storybook-builder-vite": "^0.1.12",
diff --git a/public/locales/de-DE/app.json b/public/locales/de-DE/app.json
new file mode 100644
index 00000000..63a49fa4
--- /dev/null
+++ b/public/locales/de-DE/app.json
@@ -0,0 +1,4 @@
+{
+ "Invite": "Einladen",
+ "Video call": "Videoanruf"
+}
diff --git a/public/locales/en-GB/app.json b/public/locales/en-GB/app.json
new file mode 100644
index 00000000..e9c03b03
--- /dev/null
+++ b/public/locales/en-GB/app.json
@@ -0,0 +1,135 @@
+{
+ "{{count}} people connected|one": "{{count}} person connected",
+ "{{count}} people connected|other": "{{count}} people connected",
+ "{{displayName}}, your call is now ended": "{{displayName}}, your call is now ended",
+ "{{name}} is presenting": "{{name}} is presenting",
+ "{{name}} is talking…": "{{name}} is talking…",
+ "{{names}}, {{name}}": "{{names}}, {{name}}",
+ "{{roomName}} - Walkie-talkie call": "{{roomName}} - Walkie-talkie call",
+ "<0>Already have an account?0><1><0>Log in0> Or <2>Access as a guest2>1>": "<0>Already have an account?0><1><0>Log in0> Or <2>Access as a guest2>1>",
+ "<0>Create an account0> Or <2>Access as a guest2>": "<0>Create an account0> Or <2>Access as a guest2>",
+ "<0>Oops, something's gone wrong.0><1>Submitting debug logs will help us track down the problem.1>": "<0>Oops, something's gone wrong.0><1>Submitting debug logs will help us track down the problem.1>",
+ "<0>Why not finish by setting up a password to keep your account?0><1>You'll be able to keep your name and set an avatar for use on future calls1>": "<0>Why not finish by setting up a password to keep your account?0><1>You'll be able to keep your name and set an avatar for use on future calls1>",
+ "Accept camera/microphone permissions to join the call.": "Accept camera/microphone permissions to join the call.",
+ "Accept microphone permissions to join the call.": "Accept microphone permissions to join the call.",
+ "Another user on this call is having an issue. In order to better diagnose these issues we'd like to collect a debug log.": "Another user on this call is having an issue. In order to better diagnose these issues we'd like to collect a debug log.",
+ "Audio": "Audio",
+ "Avatar": "Avatar",
+ "By clicking \"Go\", you agree to our <2>Terms and conditions2>": "By clicking \"Go\", you agree to our <2>Terms and conditions2>",
+ "By clicking \"Join call now\", you agree to our <2>Terms and conditions2>": "By clicking \"Join call now\", you agree to our <2>Terms and conditions2>",
+ "Call link copied": "Call link copied",
+ "Call type menu": "Call type menu",
+ "Camera": "Camera",
+ "Camera {{n}}": "Camera {{n}}",
+ "Camera/microphone permissions needed to join the call.": "Camera/microphone permissions needed to join the call.",
+ "Change layout": "Change layout",
+ "Close": "Close",
+ "Confirm password": "Confirm password",
+ "Connection lost": "Connection lost",
+ "Copied!": "Copied!",
+ "Copy and share this call link": "Copy and share this call link",
+ "Copy call link and join later": "Copy call link and join later",
+ "Create account": "Create account",
+ "Debug log": "Debug log",
+ "Debug log request": "Debug log request",
+ "Description (optional)": "Description (optional)",
+ "Details": "Details",
+ "Developer": "Developer",
+ "Display name": "Display name",
+ "Download debug logs": "Download debug logs",
+ "Entering room…": "Entering room…",
+ "Exit full screen": "Exit full screen",
+ "Fetching group call timed out.": "Fetching group call timed out.",
+ "Freedom": "Freedom",
+ "Full screen": "Full screen",
+ "Go": "Go",
+ "Grid layout menu": "Grid layout menu",
+ "Having trouble? Help us fix it.": "Having trouble? Help us fix it.",
+ "Home": "Home",
+ "Include debug logs": "Include debug logs",
+ "Incompatible versions": "Incompatible versions",
+ "Incompatible versions!": "Incompatible versions!",
+ "Inspector": "Inspector",
+ "Invite": "Invite",
+ "Invite people": "Invite people",
+ "Join call": "Join call",
+ "Join call now": "Join call now",
+ "Join existing call?": "Join existing call?",
+ "Leave": "Leave",
+ "Loading room…": "Loading room…",
+ "Loading…": "Loading…",
+ "Local volume": "Local volume",
+ "Logging in…": "Logging in…",
+ "Login": "Login",
+ "Login to your account": "Login to your account",
+ "Microphone": "Microphone",
+ "Microphone {{n}}": "Microphone {{n}}",
+ "Microphone permissions needed to join the call.": "Microphone permissions needed to join the call.",
+ "More": "More",
+ "More menu": "More menu",
+ "Mute microphone": "Mute microphone",
+ "No": "No",
+ "Not now, return to home screen": "Not now, return to home screen",
+ "Not registered yet? <1>Create an account1>": "Not registered yet? <1>Create an account1>",
+ "Not registered yet? <2>Create an account2>": "Not registered yet? <2>Create an account2>",
+ "Other users are trying to join this call from incompatible versions. These users should ensure that they have refreshed their browsers:<1>{userLis}1>": "Other users are trying to join this call from incompatible versions. These users should ensure that they have refreshed their browsers:<1>{userLis}1>",
+ "Password": "Password",
+ "Passwords must match": "Passwords must match",
+ "Press and hold spacebar to talk": "Press and hold spacebar to talk",
+ "Press and hold spacebar to talk over {{name}}": "Press and hold spacebar to talk over {{name}}",
+ "Press and hold to talk": "Press and hold to talk",
+ "Press and hold to talk over {{name}}": "Press and hold to talk over {{name}}",
+ "Profile": "Profile",
+ "Recaptcha dismissed": "Recaptcha dismissed",
+ "Recaptcha not loaded": "Recaptcha not loaded",
+ "Register": "Register",
+ "Registering…": "Registering…",
+ "Release spacebar key to stop": "Release spacebar key to stop",
+ "Release to stop": "Release to stop",
+ "Remove": "Remove",
+ "Return to home screen": "Return to home screen",
+ "Save": "Save",
+ "Saving…": "Saving…",
+ "Select an option": "Select an option",
+ "Send debug log": "Send debug log",
+ "Send debug logs": "Send debug logs",
+ "Sending debug log…": "Sending debug log…",
+ "Sending…": "Sending…",
+ "Settings": "Settings",
+ "Share screen": "Share screen",
+ "Show call inspector": "Show call inspector",
+ "Sign in": "Sign in",
+ "Sign out": "Sign out",
+ "Spatial audio": "Spatial audio",
+ "Speaker": "Speaker",
+ "Speaker {{n}}": "Speaker {{n}}",
+ "Spotlight": "Spotlight",
+ "Stop sharing screen": "Stop sharing screen",
+ "Submit feedback": "Submit feedback",
+ "Submitting feedback…": "Submitting feedback…",
+ "Take me Home": "Take me Home",
+ "Talk over speaker": "Talk over speaker",
+ "Talking…": "Talking…",
+ "Thanks! We'll get right on it.": "Thanks! We'll get right on it.",
+ "This call already exists, would you like to join?": "This call already exists, would you like to join?",
+ "This site is protected by ReCAPTCHA and the Google <2>Privacy Policy2> and <6>Terms of Service6> apply.<9>9>By clicking \"Register\", you agree to our <12>Terms and conditions12>": "This site is protected by ReCAPTCHA and the Google <2>Privacy Policy2> and <6>Terms of Service6> apply.<9>9>By clicking \"Register\", you agree to our <12>Terms and conditions12>",
+ "This will make a speaker's audio seem as if it is coming from where their tile is positioned on screen. (Experimental feature: this may impact the stability of audio.)": "This will make a speaker's audio seem as if it is coming from where their tile is positioned on screen. (Experimental feature: this may impact the stability of audio.)",
+ "Turn off camera": "Turn off camera",
+ "Turn on camera": "Turn on camera",
+ "Unmute microphone": "Unmute microphone",
+ "User ID": "User ID",
+ "User menu": "User menu",
+ "Username": "Username",
+ "Version: {{version}}": "Version: {{version}}",
+ "Video": "Video",
+ "Video call": "Video call",
+ "Video call name": "Video call name",
+ "Waiting for network": "Waiting for network",
+ "Waiting for other participants…": "Waiting for other participants…",
+ "Walkie-talkie call": "Walkie-talkie call",
+ "Walkie-talkie call name": "Walkie-talkie call name",
+ "WebRTC is not supported or is being blocked in this browser.": "WebRTC is not supported or is being blocked in this browser.",
+ "Yes, join call": "Yes, join call",
+ "You can't talk at the same time": "You can't talk at the same time",
+ "Your recent calls": "Your recent calls"
+}
diff --git a/src/App.tsx b/src/App.tsx
index b41271b5..02997349 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-import React from "react";
+import React, { Suspense } from "react";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import * as Sentry from "@sentry/react";
import { OverlayProvider } from "@react-aria/overlays";
@@ -43,34 +43,36 @@ export default function App({ history }: AppProps) {
return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
);
}
diff --git a/src/ClientContext.tsx b/src/ClientContext.tsx
index c026781f..3e9e6baf 100644
--- a/src/ClientContext.tsx
+++ b/src/ClientContext.tsx
@@ -27,6 +27,7 @@ import { useHistory } from "react-router-dom";
import { MatrixClient, ClientEvent } from "matrix-js-sdk/src/client";
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { logger } from "matrix-js-sdk/src/logger";
+import { useTranslation } from "react-i18next";
import { ErrorView } from "./FullScreenView";
import {
@@ -35,6 +36,7 @@ import {
CryptoStoreIntegrityError,
} from "./matrix-utils";
import { widget } from "./widget";
+import { translatedError } from "./TranslatedError";
declare global {
interface Window {
@@ -267,6 +269,8 @@ export const ClientProvider: FC = ({ children }) => {
history.push("/");
}, [history, client]);
+ const { t } = useTranslation();
+
useEffect(() => {
// To protect against multiple sessions writing to the same storage
// simultaneously, we send a to-device message that shuts down all other
@@ -287,8 +291,9 @@ export const ClientProvider: FC = ({ children }) => {
setState((prev) => ({
...prev,
- error: new Error(
- "This application has been opened in another tab."
+ error: translatedError(
+ "This application has been opened in another tab.",
+ t
),
}));
}
@@ -306,7 +311,7 @@ export const ClientProvider: FC = ({ children }) => {
client?.removeListener(ClientEvent.ToDeviceEvent, onToDeviceEvent);
};
}
- }, [client]);
+ }, [client, t]);
const context = useMemo(
() => ({
diff --git a/src/Facepile.tsx b/src/Facepile.tsx
index bd3c9c66..c3f5b290 100644
--- a/src/Facepile.tsx
+++ b/src/Facepile.tsx
@@ -14,10 +14,11 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-import React, { HTMLAttributes } from "react";
+import React, { HTMLAttributes, useMemo } from "react";
import classNames from "classnames";
import { MatrixClient } from "matrix-js-sdk/src/client";
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
+import { useTranslation } from "react-i18next";
import styles from "./Facepile.module.css";
import { Avatar, Size, sizes } from "./Avatar";
@@ -44,13 +45,25 @@ export function Facepile({
size = Size.XS,
...rest
}: Props) {
+ const { t } = useTranslation();
+
const _size = sizes.get(size);
const _overlap = overlapMap[size];
+ const title = useMemo(() => {
+ return participants.reduce(
+ (prev, curr) =>
+ prev === null
+ ? curr.name
+ : t("{{names}}, {{name}}", { names: prev, name: curr.name }),
+ null
+ ) as string;
+ }, [participants, t]);
+
return (
member.name).join(", ")}
+ title={title}
style={{
width:
Math.min(participants.length, max + 1) * (_size - _overlap) +
diff --git a/src/FullScreenView.tsx b/src/FullScreenView.tsx
index 91821116..4d4739c4 100644
--- a/src/FullScreenView.tsx
+++ b/src/FullScreenView.tsx
@@ -1,12 +1,14 @@
import React, { ReactNode, useCallback, useEffect } from "react";
import { useLocation } from "react-router-dom";
import classNames from "classnames";
+import { Trans, useTranslation } from "react-i18next";
import { Header, HeaderLogo, LeftNav, RightNav } from "./Header";
import { LinkButton, Button } from "./button";
import { useSubmitRageshake } from "./settings/submit-rageshake";
import { ErrorMessage } from "./input/Input";
import styles from "./FullScreenView.module.css";
+import { translatedError, TranslatedError } from "./TranslatedError";
interface FullScreenViewProps {
className?: string;
@@ -35,6 +37,7 @@ interface ErrorViewProps {
export function ErrorView({ error }: ErrorViewProps) {
const location = useLocation();
+ const { t } = useTranslation();
useEffect(() => {
console.error(error);
@@ -47,7 +50,11 @@ export function ErrorView({ error }: ErrorViewProps) {
return (
Error
- {error.message}
+
+ {error instanceof TranslatedError
+ ? error.translatedMessage
+ : error.message}
+
{location.pathname === "/" ? (
- Return to home screen
+ {t("Return to home screen")}
) : (
- Return to home screen
+ {t("Return to home screen")}
)}
@@ -72,6 +79,7 @@ export function ErrorView({ error }: ErrorViewProps) {
}
export function CrashView() {
+ const { t } = useTranslation();
const { submitRageshake, sending, sent, error } = useSubmitRageshake();
const sendDebugLogs = useCallback(() => {
@@ -85,11 +93,11 @@ export function CrashView() {
window.location.href = "/";
}, []);
- let logsComponent;
+ let logsComponent: JSX.Element | null = null;
if (sent) {
- logsComponent =
Thanks! We'll get right on it.
;
+ logsComponent =
{t("Thanks! We'll get right on it.")}
;
} else if (sending) {
- logsComponent =
Sending...
;
+ logsComponent =
{t("Sending…")}
;
} else {
logsComponent = (
- Send debug logs
+ {t("Send debug logs")}
);
}
return (
- Oops, something's gone wrong.
- Submitting debug logs will help us track down the problem.
+
+ Oops, something's gone wrong.
+ Submitting debug logs will help us track down the problem.
+
{logsComponent}
- {error && Couldn't send debug logs! }
+ {error && (
+
+ )}
- Return to home screen
+ {t("Return to home screen")}
);
}
export function LoadingView() {
+ const { t } = useTranslation();
+
return (
- Loading...
+ {t("Loading…")}
);
}
diff --git a/src/Header.tsx b/src/Header.tsx
index acad072f..9e4af882 100644
--- a/src/Header.tsx
+++ b/src/Header.tsx
@@ -4,6 +4,7 @@ import { Link } from "react-router-dom";
import { useButton } from "@react-aria/button";
import { AriaButtonProps } from "@react-types/button";
import { Room } from "matrix-js-sdk/src/models/room";
+import { useTranslation } from "react-i18next";
import styles from "./Header.module.css";
import { useModalTriggerState } from "./Modal";
@@ -156,6 +157,7 @@ export function VersionMismatchWarning({
users,
room,
}: VersionMismatchWarningProps) {
+ const { t } = useTranslation();
const { modalState, modalProps } = useModalTriggerState();
const onDetailsClick = useCallback(() => {
@@ -166,9 +168,9 @@ export function VersionMismatchWarning({
return (
- Incomaptible versions!
+ {t("Incompatible versions!")}
- Details
+ {t("Details")}
{modalState.isOpen && (
diff --git a/src/IncompatibleVersionModal.tsx b/src/IncompatibleVersionModal.tsx
index 637859e3..29abfb9d 100644
--- a/src/IncompatibleVersionModal.tsx
+++ b/src/IncompatibleVersionModal.tsx
@@ -15,7 +15,8 @@ limitations under the License.
*/
import { Room } from "matrix-js-sdk/src/models/room";
-import React from "react";
+import React, { useMemo } from "react";
+import { Trans, useTranslation } from "react-i18next";
import { Modal, ModalContent } from "./Modal";
import { Body } from "./typography/Typography";
@@ -30,17 +31,21 @@ export const IncompatibleVersionModal: React.FC = ({
room,
...rest
}) => {
- const userLis = Array.from(userIds).map((u) => (
- {room.getMember(u).name}
- ));
+ const { t } = useTranslation();
+ const userLis = useMemo(
+ () => [...userIds].map((u) => {room.getMember(u)?.name ?? u} ),
+ [userIds, room]
+ );
return (
-
+
- Other users are trying to join this call from incompatible versions.
- These users should ensure that they have refreshed their browsers:
-
+
+ Other users are trying to join this call from incompatible versions.
+ These users should ensure that they have refreshed their browsers:
+
+
diff --git a/src/Modal.tsx b/src/Modal.tsx
index 686234a0..8a41fbc8 100644
--- a/src/Modal.tsx
+++ b/src/Modal.tsx
@@ -33,6 +33,7 @@ import { FocusScope } from "@react-aria/focus";
import { ButtonAria, useButton } from "@react-aria/button";
import classNames from "classnames";
import { AriaDialogProps } from "@react-types/dialog";
+import { useTranslation } from "react-i18next";
import { ReactComponent as CloseIcon } from "./icons/Close.svg";
import styles from "./Modal.module.css";
@@ -53,6 +54,7 @@ export function Modal({
onClose,
...rest
}: ModalProps) {
+ const { t } = useTranslation();
const modalRef = useRef();
const { overlayProps, underlayProps } = useOverlay(
{ ...rest, onClose },
@@ -90,6 +92,7 @@ export function Modal({
{...closeButtonProps}
ref={closeButtonRef}
className={styles.closeButton}
+ title={t("Close")}
>
diff --git a/src/SequenceDiagramViewerPage.tsx b/src/SequenceDiagramViewerPage.tsx
index a6473ccc..e04837db 100644
--- a/src/SequenceDiagramViewerPage.tsx
+++ b/src/SequenceDiagramViewerPage.tsx
@@ -15,6 +15,7 @@ limitations under the License.
*/
import React, { useCallback, useState } from "react";
+import { useTranslation } from "react-i18next";
import {
SequenceDiagramViewer,
@@ -30,7 +31,8 @@ interface DebugLog {
}
export function SequenceDiagramViewerPage() {
- usePageTitle("Inspector");
+ const { t } = useTranslation();
+ usePageTitle(t("Inspector"));
const [debugLog, setDebugLog] = useState();
const [selectedUserId, setSelectedUserId] = useState();
@@ -49,7 +51,7 @@ export function SequenceDiagramViewerPage() {
type="file"
id="debugLog"
name="debugLog"
- label="Debug Log"
+ label={t("Debug log")}
onChange={onChangeDebugLog}
/>
diff --git a/src/TranslatedError.ts b/src/TranslatedError.ts
new file mode 100644
index 00000000..62960f04
--- /dev/null
+++ b/src/TranslatedError.ts
@@ -0,0 +1,41 @@
+/*
+Copyright 2022 New Vector Ltd
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import i18n from "i18next";
+
+/**
+ * An error with messages in both English and the user's preferred language.
+ */
+// Abstract to force consumers to use the function below rather than calling the
+// constructor directly
+export abstract class TranslatedError extends Error {
+ /**
+ * The error message in the user's preferred language.
+ */
+ public readonly translatedMessage: string;
+
+ public constructor(messageKey: string, translationFn: typeof i18n.t) {
+ super(translationFn(messageKey, { lng: "en-GB" }));
+ this.translatedMessage = translationFn(messageKey);
+ }
+}
+
+class TranslatedErrorImpl extends TranslatedError {}
+
+// i18next-parser can't detect calls to a constructor, so we expose a bare
+// function instead
+export const translatedError = (messageKey: string, t: typeof i18n.t) =>
+ new TranslatedErrorImpl(messageKey, t);
diff --git a/src/room/useRoomParams.ts b/src/UrlParams.ts
similarity index 82%
rename from src/room/useRoomParams.ts
rename to src/UrlParams.ts
index 095bf5fd..84e2312f 100644
--- a/src/room/useRoomParams.ts
+++ b/src/UrlParams.ts
@@ -17,7 +17,7 @@ limitations under the License.
import { useMemo } from "react";
import { useLocation } from "react-router-dom";
-export interface RoomParams {
+export interface UrlParams {
roomAlias: string | null;
roomId: string | null;
viaServers: string[];
@@ -39,25 +39,27 @@ export interface RoomParams {
displayName: string | null;
// The device's ID (only used in Matroska mode)
deviceId: string | null;
+ // The BCP 47 code of the language the app should use
+ lang: string | null;
}
/**
- * Gets the room parameters for the current URL.
- * @param {string} query The URL query string
- * @param {string} fragment The URL fragment string
- * @returns {RoomParams} The room parameters encoded in the URL
+ * Gets the app parameters for the current URL.
+ * @param query The URL query string
+ * @param fragment The URL fragment string
+ * @returns The app parameters encoded in the URL
*/
-export const getRoomParams = (
+export const getUrlParams = (
query: string = window.location.search,
fragment: string = window.location.hash
-): RoomParams => {
+): UrlParams => {
const fragmentQueryStart = fragment.indexOf("?");
const fragmentParams = new URLSearchParams(
fragmentQueryStart === -1 ? "" : fragment.substring(fragmentQueryStart)
);
const queryParams = new URLSearchParams(query);
- // Normally, room params should be encoded in the fragment so as to avoid
+ // Normally, URL params should be encoded in the fragment so as to avoid
// leaking them to the server. However, we also check the normal query
// string for backwards compatibility with versions that only used that.
const hasParam = (name: string): boolean =>
@@ -87,14 +89,15 @@ export const getRoomParams = (
userId: getParam("userId"),
displayName: getParam("displayName"),
deviceId: getParam("deviceId"),
+ lang: getParam("lang"),
};
};
/**
- * Hook to simplify use of getRoomParams.
- * @returns {RoomParams} The room parameters for the current URL
+ * Hook to simplify use of getUrlParams.
+ * @returns The app parameters for the current URL
*/
-export const useRoomParams = (): RoomParams => {
+export const useUrlParams = (): UrlParams => {
const { hash, search } = useLocation();
- return useMemo(() => getRoomParams(search, hash), [search, hash]);
+ return useMemo(() => getUrlParams(search, hash), [search, hash]);
};
diff --git a/src/UserMenu.tsx b/src/UserMenu.tsx
index 83da187a..fac4a867 100644
--- a/src/UserMenu.tsx
+++ b/src/UserMenu.tsx
@@ -1,6 +1,7 @@
-import React, { useMemo } from "react";
+import React, { useCallback, useMemo } from "react";
import { Item } from "@react-stately/collections";
import { useLocation } from "react-router-dom";
+import { useTranslation } from "react-i18next";
import { Button, LinkButton } from "./button";
import { PopoverMenuTrigger } from "./popover/PopoverMenu";
@@ -30,6 +31,7 @@ export function UserMenu({
avatarUrl,
onAction,
}: UserMenuProps) {
+ const { t } = useTranslation();
const location = useLocation();
const items = useMemo(() => {
@@ -45,7 +47,7 @@ export function UserMenu({
if (isPasswordlessUser && !preventNavigation) {
arr.push({
key: "login",
- label: "Sign In",
+ label: t("Sign in"),
icon: LoginIcon,
});
}
@@ -53,14 +55,16 @@ export function UserMenu({
if (!isPasswordlessUser && !preventNavigation) {
arr.push({
key: "logout",
- label: "Sign Out",
+ label: t("Sign out"),
icon: LogoutIcon,
});
}
}
return arr;
- }, [isAuthenticated, isPasswordlessUser, displayName, preventNavigation]);
+ }, [isAuthenticated, isPasswordlessUser, displayName, preventNavigation, t]);
+
+ const tooltip = useCallback(() => t("Profile"), [t]);
if (!isAuthenticated) {
return (
@@ -72,7 +76,7 @@ export function UserMenu({
return (
- "Profile"} placement="bottom left">
+
{isAuthenticated && (!isPasswordlessUser || avatarUrl) ? (
{(props) => (
-
+
{items.map(({ key, icon: Icon, label }) => (
-
diff --git a/src/auth/LoginPage.tsx b/src/auth/LoginPage.tsx
index 8ef1ca5d..b7ea8349 100644
--- a/src/auth/LoginPage.tsx
+++ b/src/auth/LoginPage.tsx
@@ -23,6 +23,7 @@ import React, {
useMemo,
} from "react";
import { useHistory, useLocation, Link } from "react-router-dom";
+import { Trans, useTranslation } from "react-i18next";
import { ReactComponent as Logo } from "../icons/LogoLarge.svg";
import { useClient } from "../ClientContext";
@@ -34,7 +35,8 @@ import { useInteractiveLogin } from "./useInteractiveLogin";
import { usePageTitle } from "../usePageTitle";
export const LoginPage: FC = () => {
- usePageTitle("Login");
+ const { t } = useTranslation();
+ usePageTitle(t("Login"));
const { setClient } = useClient();
const login = useInteractiveLogin();
@@ -93,8 +95,8 @@ export const LoginPage: FC = () => {
{
{error && (
- {error.message}
+
)}
- {loading ? "Logging in..." : "Login"}
+ {loading ? t("Logging in…") : t("Login")}
@@ -124,9 +126,11 @@ export const LoginPage: FC = () => {
Not registered yet?
- Create an account
- {" Or "}
- Access as a guest
+
+ Create an account
+ {" Or "}
+ Access as a guest
+
diff --git a/src/auth/RegisterPage.tsx b/src/auth/RegisterPage.tsx
index 0f908beb..f8583b74 100644
--- a/src/auth/RegisterPage.tsx
+++ b/src/auth/RegisterPage.tsx
@@ -26,6 +26,7 @@ import React, {
import { useHistory, useLocation } from "react-router-dom";
import { captureException } from "@sentry/react";
import { sleep } from "matrix-js-sdk/src/utils";
+import { Trans, useTranslation } from "react-i18next";
import { FieldRow, InputField, ErrorMessage } from "../input/Input";
import { Button } from "../button";
@@ -40,7 +41,8 @@ import { Caption, Link } from "../typography/Typography";
import { usePageTitle } from "../usePageTitle";
export const RegisterPage: FC = () => {
- usePageTitle("Register");
+ const { t } = useTranslation();
+ usePageTitle(t("Register"));
const { loading, isAuthenticated, isPasswordlessUser, client, setClient } =
useClient();
@@ -126,11 +128,11 @@ export const RegisterPage: FC = () => {
useEffect(() => {
if (password && passwordConfirmation && password !== passwordConfirmation) {
- confirmPasswordRef.current?.setCustomValidity("Passwords must match");
+ confirmPasswordRef.current?.setCustomValidity(t("Passwords must match"));
} else {
confirmPasswordRef.current?.setCustomValidity("");
}
- }, [password, passwordConfirmation]);
+ }, [password, passwordConfirmation, t]);
useEffect(() => {
if (!loading && isAuthenticated && !isPasswordlessUser && !registering) {
@@ -154,8 +156,8 @@ export const RegisterPage: FC = () => {
{
setPassword(e.target.value)
}
value={password}
- placeholder="Password"
- label="Password"
+ placeholder={t("Password")}
+ label={t("Password")}
/>
@@ -184,45 +186,49 @@ export const RegisterPage: FC = () => {
setPasswordConfirmation(e.target.value)
}
value={passwordConfirmation}
- placeholder="Confirm Password"
- label="Confirm Password"
+ placeholder={t("Confirm password")}
+ label={t("Confirm password")}
ref={confirmPasswordRef}
/>
- This site is protected by ReCAPTCHA and the Google{" "}
-
- Privacy Policy
- {" "}
- and{" "}
-
- Terms of Service
- {" "}
- apply.
-
- By clicking "Register", you agree to our{" "}
- Terms and conditions
+
+ This site is protected by ReCAPTCHA and the Google{" "}
+
+ Privacy Policy
+ {" "}
+ and{" "}
+
+ Terms of Service
+ {" "}
+ apply.
+
+ By clicking "Register", you agree to our{" "}
+ Terms and conditions
+
{error && (
- {error.message}
+
)}
- {registering ? "Registering..." : "Register"}
+ {registering ? t("Registering…") : t("Register")}
-
Already have an account?
-
- Log in
- {" Or "}
- Access as a guest
-
+
+ Already have an account?
+
+ Log in
+ {" Or "}
+ Access as a guest
+
+
diff --git a/src/auth/useRecaptcha.ts b/src/auth/useRecaptcha.ts
index 76856a23..50d23c8c 100644
--- a/src/auth/useRecaptcha.ts
+++ b/src/auth/useRecaptcha.ts
@@ -16,6 +16,9 @@ limitations under the License.
import { useEffect, useCallback, useRef, useState } from "react";
import { randomString } from "matrix-js-sdk/src/randomstring";
+import { useTranslation } from "react-i18next";
+
+import { translatedError } from "../TranslatedError";
declare global {
interface Window {
@@ -32,6 +35,7 @@ interface RecaptchaPromiseRef {
}
export const useRecaptcha = (sitekey: string) => {
+ const { t } = useTranslation();
const [recaptchaId] = useState(() => randomString(16));
const promiseRef = useRef();
@@ -71,14 +75,14 @@ export const useRecaptcha = (sitekey: string) => {
if (!window.grecaptcha) {
console.log("Recaptcha not loaded");
- return Promise.reject(new Error("Recaptcha not loaded"));
+ return Promise.reject(translatedError("Recaptcha not loaded", t));
}
return new Promise((resolve, reject) => {
const observer = new MutationObserver((mutationsList) => {
for (const item of mutationsList) {
if ((item.target as HTMLElement)?.style?.visibility !== "visible") {
- reject(new Error("Recaptcha dismissed"));
+ reject(translatedError("Recaptcha dismissed", t));
observer.disconnect();
return;
}
@@ -108,7 +112,7 @@ export const useRecaptcha = (sitekey: string) => {
});
}
});
- }, [sitekey]);
+ }, [sitekey, t]);
const reset = useCallback(() => {
window.grecaptcha?.reset();
diff --git a/src/button/Button.tsx b/src/button/Button.tsx
index c29b5f24..80220ff8 100644
--- a/src/button/Button.tsx
+++ b/src/button/Button.tsx
@@ -18,6 +18,7 @@ import { PressEvent } from "@react-types/shared";
import classNames from "classnames";
import { useButton } from "@react-aria/button";
import { mergeProps, useObjectRef } from "@react-aria/utils";
+import { useTranslation } from "react-i18next";
import styles from "./Button.module.css";
import { ReactComponent as MicIcon } from "../icons/Mic.svg";
@@ -142,9 +143,11 @@ export function MicButton({
// TODO: add all props for
[index: string]: unknown;
}) {
+ const { t } = useTranslation();
+
return (
(muted ? "Unmute microphone" : "Mute microphone")}
+ tooltip={() => (muted ? t("Unmute microphone") : t("Mute microphone"))}
>
{muted ? : }
@@ -161,9 +164,11 @@ export function VideoButton({
// TODO: add all props for
[index: string]: unknown;
}) {
+ const { t } = useTranslation();
+
return (
(muted ? "Turn on camera" : "Turn off camera")}
+ tooltip={() => (muted ? t("Turn on camera") : t("Turn off camera"))}
>
{muted ? : }
@@ -182,9 +187,11 @@ export function ScreenshareButton({
// TODO: add all props for
[index: string]: unknown;
}) {
+ const { t } = useTranslation();
+
return (
(enabled ? "Stop sharing screen" : "Share screen")}
+ tooltip={() => (enabled ? t("Stop sharing screen") : t("Share screen"))}
>
@@ -201,8 +208,11 @@ export function HangupButton({
// TODO: add all props for
[index: string]: unknown;
}) {
+ const { t } = useTranslation();
+ const tooltip = useCallback(() => t("Leave"), [t]);
+
return (
- "Leave"}>
+
[index: string]: unknown;
}) {
+ const { t } = useTranslation();
+ const tooltip = useCallback(() => t("Settings"), [t]);
+
return (
- "Settings"}>
+
@@ -239,8 +252,11 @@ export function InviteButton({
// TODO: add all props for
[index: string]: unknown;
}) {
+ const { t } = useTranslation();
+ const tooltip = useCallback(() => t("Invite"), [t]);
+
return (
- "Invite"}>
+
@@ -256,8 +272,11 @@ interface AudioButtonProps extends Omit {
}
export function AudioButton({ volume, ...rest }: AudioButtonProps) {
+ const { t } = useTranslation();
+ const tooltip = useCallback(() => t("Local volume"), [t]);
+
return (
- "Local volume"}>
+
@@ -273,12 +292,13 @@ export function FullscreenButton({
fullscreen,
...rest
}: FullscreenButtonProps) {
- const getTooltip = useCallback(() => {
- return fullscreen ? "Exit full screen" : "Full screen";
- }, [fullscreen]);
+ const { t } = useTranslation();
+ const tooltip = useCallback(() => {
+ return fullscreen ? t("Exit full screen") : t("Full screen");
+ }, [fullscreen, t]);
return (
-
+
{fullscreen ? : }
diff --git a/src/button/CopyButton.tsx b/src/button/CopyButton.tsx
index d6f159ea..5453f0af 100644
--- a/src/button/CopyButton.tsx
+++ b/src/button/CopyButton.tsx
@@ -15,6 +15,7 @@ limitations under the License.
*/
import React from "react";
+import { useTranslation } from "react-i18next";
import useClipboard from "react-use-clipboard";
import { ReactComponent as CheckIcon } from "../icons/Check.svg";
@@ -36,6 +37,7 @@ export function CopyButton({
copiedMessage,
...rest
}: Props) {
+ const { t } = useTranslation();
const [isCopied, setCopied] = useClipboard(value, { successDuration: 3000 });
return (
@@ -49,7 +51,7 @@ export function CopyButton({
>
{isCopied ? (
<>
- {variant !== "icon" && {copiedMessage || "Copied!"} }
+ {variant !== "icon" && {copiedMessage || t("Copied!")} }
>
) : (
diff --git a/src/home/CallTypeDropdown.tsx b/src/home/CallTypeDropdown.tsx
index 2713e4d0..5ee4ec24 100644
--- a/src/home/CallTypeDropdown.tsx
+++ b/src/home/CallTypeDropdown.tsx
@@ -16,6 +16,7 @@ limitations under the License.
import React, { FC } from "react";
import { Item } from "@react-stately/collections";
+import { useTranslation } from "react-i18next";
import { Headline } from "../typography/Typography";
import { Button } from "../button";
@@ -39,25 +40,29 @@ interface Props {
}
export const CallTypeDropdown: FC = ({ callType, setCallType }) => {
+ const { t } = useTranslation();
+
return (
- {callType === CallType.Video ? "Video call" : "Walkie-talkie call"}
+ {callType === CallType.Video
+ ? t("Video call")
+ : t("Walkie-talkie call")}
{(props: JSX.IntrinsicAttributes) => (
-
- -
+
+ -
- Video call
+ {t("Video call")}
{callType === CallType.Video && (
)}
- -
+
-
- Walkie-talkie call
+ {t("Walkie-talkie call")}
{callType === CallType.Radio && (
)}
diff --git a/src/home/HomePage.tsx b/src/home/HomePage.tsx
index 00f770fd..7ff6efcc 100644
--- a/src/home/HomePage.tsx
+++ b/src/home/HomePage.tsx
@@ -15,6 +15,7 @@ limitations under the License.
*/
import React from "react";
+import { useTranslation } from "react-i18next";
import { useClient } from "../ClientContext";
import { ErrorView, LoadingView } from "../FullScreenView";
@@ -23,7 +24,8 @@ import { RegisteredView } from "./RegisteredView";
import { usePageTitle } from "../usePageTitle";
export function HomePage() {
- usePageTitle("Home");
+ const { t } = useTranslation();
+ usePageTitle(t("Home"));
const { isAuthenticated, isPasswordlessUser, loading, error, client } =
useClient();
diff --git a/src/home/JoinExistingCallModal.tsx b/src/home/JoinExistingCallModal.tsx
index b26c45d9..8f4f7d7f 100644
--- a/src/home/JoinExistingCallModal.tsx
+++ b/src/home/JoinExistingCallModal.tsx
@@ -16,6 +16,7 @@ limitations under the License.
import React from "react";
import { PressEvent } from "@react-types/shared";
+import { useTranslation } from "react-i18next";
import { Modal, ModalContent } from "../Modal";
import { Button } from "../button";
@@ -29,13 +30,15 @@ interface Props {
[index: string]: unknown;
}
export function JoinExistingCallModal({ onJoin, onClose, ...rest }: Props) {
+ const { t } = useTranslation();
+
return (
-
+
- This call already exists, would you like to join?
+ {t("This call already exists, would you like to join?")}
- No
- Yes, join call
+ {t("No")}
+ {t("Yes, join call")}
diff --git a/src/home/RegisteredView.tsx b/src/home/RegisteredView.tsx
index 71abb281..af72e319 100644
--- a/src/home/RegisteredView.tsx
+++ b/src/home/RegisteredView.tsx
@@ -22,6 +22,7 @@ import React, {
} from "react";
import { useHistory } from "react-router-dom";
import { MatrixClient } from "matrix-js-sdk/src/client";
+import { useTranslation } from "react-i18next";
import { createRoom, roomAliasLocalpartFromRoomName } from "../matrix-utils";
import { useGroupCallRooms } from "./useGroupCallRooms";
@@ -48,6 +49,7 @@ export function RegisteredView({ client, isPasswordlessUser }: Props) {
const [loading, setLoading] = useState(false);
const [error, setError] = useState();
const history = useHistory();
+ const { t } = useTranslation();
const { modalState, modalProps } = useModalTriggerState();
const onSubmit: FormEventHandler = useCallback(
@@ -93,7 +95,9 @@ export function RegisteredView({ client, isPasswordlessUser }: Props) {
}, [history, existingRoomId]);
const callNameLabel =
- callType === CallType.Video ? "Video call name" : "Walkie-talkie call name";
+ callType === CallType.Video
+ ? t("Video call name")
+ : t("Walkie-talkie call name");
return (
<>
@@ -127,19 +131,19 @@ export function RegisteredView({ client, isPasswordlessUser }: Props) {
className={styles.button}
disabled={loading}
>
- {loading ? "Loading..." : "Go"}
+ {loading ? t("Loading…") : t("Go")}
{error && (
- {error.message}
+
)}
{recentRooms.length > 0 && (
<>
- Your recent Calls
+ {t("Your recent calls")}
>
diff --git a/src/home/UnauthenticatedView.tsx b/src/home/UnauthenticatedView.tsx
index 2cf09d3c..a84a7b2b 100644
--- a/src/home/UnauthenticatedView.tsx
+++ b/src/home/UnauthenticatedView.tsx
@@ -17,6 +17,7 @@ limitations under the License.
import React, { FC, useCallback, useState, FormEventHandler } from "react";
import { useHistory } from "react-router-dom";
import { randomString } from "matrix-js-sdk/src/randomstring";
+import { Trans, useTranslation } from "react-i18next";
import { useClient } from "../ClientContext";
import { Header, HeaderLogo, LeftNav, RightNav } from "../Header";
@@ -47,6 +48,7 @@ export const UnauthenticatedView: FC = () => {
const { modalState, modalProps } = useModalTriggerState();
const [onFinished, setOnFinished] = useState<() => void>();
const history = useHistory();
+ const { t } = useTranslation();
const onSubmit: FormEventHandler = useCallback(
(e) => {
@@ -105,7 +107,9 @@ export const UnauthenticatedView: FC = () => {
);
const callNameLabel =
- callType === CallType.Video ? "Video call name" : "Walkie-talkie call name";
+ callType === CallType.Video
+ ? t("Video call name")
+ : t("Walkie-talkie call name");
return (
<>
@@ -137,24 +141,26 @@ export const UnauthenticatedView: FC = () => {
- By clicking "Go", you agree to our{" "}
- Terms and conditions
+
+ By clicking "Go", you agree to our{" "}
+ Terms and conditions
+
{error && (
- {error.message}
+
)}
- {loading ? "Loading..." : "Go"}
+ {loading ? t("Loading…") : t("Go")}
@@ -162,14 +168,16 @@ export const UnauthenticatedView: FC = () => {
- Login to your account
+ {t("Login to your account")}
- Not registered yet?{" "}
-
- Create an account
-
+
+ Not registered yet?{" "}
+
+ Create an account
+
+
diff --git a/src/input/AvatarInputField.tsx b/src/input/AvatarInputField.tsx
index 8f0aa4d8..a9778396 100644
--- a/src/input/AvatarInputField.tsx
+++ b/src/input/AvatarInputField.tsx
@@ -20,6 +20,7 @@ import { useCallback } from "react";
import { useState } from "react";
import { forwardRef } from "react";
import classNames from "classnames";
+import { useTranslation } from "react-i18next";
import { Avatar, Size } from "../Avatar";
import { Button } from "../button";
@@ -39,6 +40,8 @@ export const AvatarInputField = forwardRef(
{ id, label, className, avatarUrl, displayName, onRemoveAvatar, ...rest },
ref
) => {
+ const { t } = useTranslation();
+
const [removed, setRemoved] = useState(false);
const [objUrl, setObjUrl] = useState(null);
@@ -97,7 +100,7 @@ export const AvatarInputField = forwardRef(
variant="icon"
onPress={onPressRemoveAvatar}
>
- Remove
+ {t("Remove")}
)}
diff --git a/src/input/Input.tsx b/src/input/Input.tsx
index 5b6ec665..cd7603fa 100644
--- a/src/input/Input.tsx
+++ b/src/input/Input.tsx
@@ -14,11 +14,12 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-import React, { ChangeEvent, forwardRef, ReactNode } from "react";
+import React, { ChangeEvent, FC, forwardRef, ReactNode } from "react";
import classNames from "classnames";
import styles from "./Input.module.css";
import { ReactComponent as CheckIcon } from "../icons/Check.svg";
+import { TranslatedError } from "../TranslatedError";
interface FieldRowProps {
children: ReactNode;
@@ -140,10 +141,12 @@ export const InputField = forwardRef<
}
);
-export function ErrorMessage({
- children,
-}: {
- children: ReactNode;
-}): JSX.Element {
- return {children}
;
+interface ErrorMessageProps {
+ error: Error;
}
+
+export const ErrorMessage: FC = ({ error }) => (
+
+ {error instanceof TranslatedError ? error.translatedMessage : error.message}
+
+);
diff --git a/src/input/SelectInput.tsx b/src/input/SelectInput.tsx
index c31d9c34..e3c47741 100644
--- a/src/input/SelectInput.tsx
+++ b/src/input/SelectInput.tsx
@@ -19,6 +19,7 @@ import { AriaSelectOptions, HiddenSelect, useSelect } from "@react-aria/select";
import { useButton } from "@react-aria/button";
import { useSelectState } from "@react-stately/select";
import classNames from "classnames";
+import { useTranslation } from "react-i18next";
import { Popover } from "../popover/Popover";
import { ListBox } from "../ListBox";
@@ -30,6 +31,7 @@ interface Props extends AriaSelectOptions {
}
export function SelectInput(props: Props): JSX.Element {
+ const { t } = useTranslation();
const state = useSelectState(props);
const ref = useRef();
@@ -56,7 +58,7 @@ export function SelectInput(props: Props): JSX.Element {
{state.selectedItem
? state.selectedItem.rendered
- : "Select an option"}
+ : t("Select an option")}
diff --git a/src/main.tsx b/src/main.tsx
index a1739c7b..4b4f0809 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -25,10 +25,15 @@ import ReactDOM from "react-dom";
import { createBrowserHistory } from "history";
import * as Sentry from "@sentry/react";
import { Integrations } from "@sentry/tracing";
+import i18n from "i18next";
+import { initReactI18next } from "react-i18next";
+import Backend from "i18next-http-backend";
+import LanguageDetector from "i18next-browser-languagedetector";
import "./index.css";
import App from "./App";
import { init as initRageshake } from "./settings/rageshake";
+import { getUrlParams } from "./UrlParams";
initRageshake();
@@ -104,6 +109,35 @@ Sentry.init({
tracesSampleRate: 1.0,
});
+const languageDetector = new LanguageDetector();
+languageDetector.addDetector({
+ name: "urlFragment",
+ // Look for a language code in the URL's fragment
+ lookup: () => getUrlParams().lang ?? undefined,
+});
+
+i18n
+ .use(Backend)
+ .use(languageDetector)
+ .use(initReactI18next)
+ .init({
+ fallbackLng: "en-GB",
+ defaultNS: "app",
+ keySeparator: false,
+ nsSeparator: false,
+ pluralSeparator: "|",
+ contextSeparator: "|",
+ interpolation: {
+ escapeValue: false, // React has built-in XSS protections
+ },
+ detection: {
+ // No localStorage detectors or caching here, since we don't have any way
+ // of letting the user manually select a language
+ order: ["urlFragment", "navigator"],
+ caches: [],
+ },
+ });
+
ReactDOM.render(
diff --git a/src/matrix-utils.ts b/src/matrix-utils.ts
index 3d93a339..282e1b75 100644
--- a/src/matrix-utils.ts
+++ b/src/matrix-utils.ts
@@ -19,7 +19,7 @@ import {
import type { MatrixClient } from "matrix-js-sdk/src/client";
import type { Room } from "matrix-js-sdk/src/models/room";
import IndexedDBWorker from "./IndexedDBWorker?worker";
-import { getRoomParams } from "./room/useRoomParams";
+import { getUrlParams } from "./UrlParams";
export const defaultHomeserver =
(import.meta.env.VITE_DEFAULT_HOMESERVER as string) ??
@@ -134,12 +134,12 @@ export async function initClient(
storeOpts.cryptoStore = new MemoryCryptoStore();
}
- // XXX: we read from the room params in RoomPage too:
+ // XXX: we read from the URL params in RoomPage too:
// it would be much better to read them in one place and pass
// the values around, but we initialise the matrix client in
// many different places so we'd have to pass it into all of
// them.
- const { e2eEnabled } = getRoomParams();
+ const { e2eEnabled } = getUrlParams();
if (!e2eEnabled) {
logger.info("Disabling E2E: group call signalling will NOT be encrypted.");
}
diff --git a/src/profile/ProfileModal.tsx b/src/profile/ProfileModal.tsx
index 89f967b8..28fc9977 100644
--- a/src/profile/ProfileModal.tsx
+++ b/src/profile/ProfileModal.tsx
@@ -16,6 +16,7 @@ limitations under the License.
import React, { ChangeEvent, useCallback, useEffect, useState } from "react";
import { MatrixClient } from "matrix-js-sdk/src/client";
+import { useTranslation } from "react-i18next";
import { Button } from "../button";
import { useProfile } from "./useProfile";
@@ -31,6 +32,7 @@ interface Props {
}
export function ProfileModal({ client, ...rest }: Props) {
const { onClose } = rest;
+ const { t } = useTranslation();
const {
success,
error,
@@ -83,14 +85,14 @@ export function ProfileModal({ client, ...rest }: Props) {
}, [success, onClose]);
return (
-
+
- {loading ? "Saving..." : "Save"}
+ {loading ? t("Saving…") : t("Save")}
diff --git a/src/room/AudioPreview.tsx b/src/room/AudioPreview.tsx
index c0e2e8b4..8d6f164d 100644
--- a/src/room/AudioPreview.tsx
+++ b/src/room/AudioPreview.tsx
@@ -17,6 +17,7 @@ limitations under the License.
import React from "react";
import { GroupCallState } from "matrix-js-sdk/src/webrtc/groupCall";
import { Item } from "@react-stately/collections";
+import { useTranslation } from "react-i18next";
import styles from "./AudioPreview.module.css";
import { SelectInput } from "../input/SelectInput";
@@ -43,24 +44,26 @@ export function AudioPreview({
audioOutputs,
setAudioOutput,
}: Props) {
+ const { t } = useTranslation();
+
return (
<>
- {`${roomName} - Walkie-talkie call`}
+ {t("{{roomName}} - Walkie-talkie call", { roomName })}
{state === GroupCallState.LocalCallFeedUninitialized && (
- Microphone permissions needed to join the call.
+ {t("Microphone permissions needed to join the call.")}
)}
{state === GroupCallState.InitializingLocalCallFeed && (
- Accept microphone permissions to join the call.
+ {t("Accept microphone permissions to join the call.")}
)}
{state === GroupCallState.LocalCallFeedInitialized && (
<>
{!!label && label.trim().length > 0
? label
- : `Microphone ${index + 1}`}
+ : t("Microphone {{n}}", { n: index + 1 })}
))}
{audioOutputs.length > 0 && (
{!!label && label.trim().length > 0
? label
- : `Speaker ${index + 1}`}
+ : t("Speaker {{n}}", { n: index + 1 })}
))}
diff --git a/src/room/CallEndedView.tsx b/src/room/CallEndedView.tsx
index c7371d66..b78b800d 100644
--- a/src/room/CallEndedView.tsx
+++ b/src/room/CallEndedView.tsx
@@ -16,6 +16,7 @@ limitations under the License.
import React from "react";
import { MatrixClient } from "matrix-js-sdk/src/client";
+import { Trans, useTranslation } from "react-i18next";
import styles from "./CallEndedView.module.css";
import { LinkButton } from "../button";
@@ -24,6 +25,7 @@ import { Subtitle, Body, Link, Headline } from "../typography/Typography";
import { Header, HeaderLogo, LeftNav, RightNav } from "../Header";
export function CallEndedView({ client }: { client: MatrixClient }) {
+ const { t } = useTranslation();
const { displayName } = useProfile(client);
return (
@@ -37,29 +39,31 @@ export function CallEndedView({ client }: { client: MatrixClient }) {
- {displayName}, your call is now ended
+ {t("{{displayName}}, your call is now ended", { displayName })}
-
- Why not finish by setting up a password to keep your account?
-
-
- You'll be able to keep your name and set an avatar for use on
- future calls
-
+
+
+ Why not finish by setting up a password to keep your account?
+
+
+ You'll be able to keep your name and set an avatar for use on
+ future calls
+
+
- Create account
+ {t("Create account")}
- Not now, return to home screen
+ {t("Not now, return to home screen")}
diff --git a/src/room/FeedbackModal.tsx b/src/room/FeedbackModal.tsx
index 7b452d85..db0f34f6 100644
--- a/src/room/FeedbackModal.tsx
+++ b/src/room/FeedbackModal.tsx
@@ -16,6 +16,7 @@ limitations under the License.
import React, { useCallback, useEffect } from "react";
import { randomString } from "matrix-js-sdk/src/randomstring";
+import { useTranslation } from "react-i18next";
import { Modal, ModalContent } from "../Modal";
import { Button } from "../button";
@@ -25,6 +26,7 @@ import {
useRageshakeRequest,
} from "../settings/submit-rageshake";
import { Body } from "../typography/Typography";
+
interface Props {
inCall: boolean;
roomId: string;
@@ -32,7 +34,9 @@ interface Props {
// TODO: add all props for for
[index: string]: unknown;
}
+
export function FeedbackModal({ inCall, roomId, onClose, ...rest }: Props) {
+ const { t } = useTranslation();
const { submitRageshake, sending, sent, error } = useSubmitRageshake();
const sendRageshakeRequest = useRageshakeRequest();
@@ -67,15 +71,20 @@ export function FeedbackModal({ inCall, roomId, onClose, ...rest }: Props) {
}, [sent, onClose]);
return (
-
+
- Having trouble? Help us fix it.
+ {t("Having trouble? Help us fix it.")}
diff --git a/src/room/GridLayoutMenu.tsx b/src/room/GridLayoutMenu.tsx
index 6b00c44e..081af4e4 100644
--- a/src/room/GridLayoutMenu.tsx
+++ b/src/room/GridLayoutMenu.tsx
@@ -14,8 +14,9 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-import React from "react";
+import React, { useCallback } from "react";
import { Item } from "@react-stately/collections";
+import { useTranslation } from "react-i18next";
import { Button } from "../button";
import { PopoverMenuTrigger } from "../popover/PopoverMenu";
@@ -27,28 +28,33 @@ import { Menu } from "../Menu";
import { TooltipTrigger } from "../Tooltip";
export type Layout = "freedom" | "spotlight";
+
interface Props {
layout: Layout;
setLayout: (layout: Layout) => void;
}
+
export function GridLayoutMenu({ layout, setLayout }: Props) {
+ const { t } = useTranslation();
+ const tooltip = useCallback(() => t("Change layout"), [t]);
+
return (
- "Layout Type"}>
+
{layout === "spotlight" ? : }
{(props: JSX.IntrinsicAttributes) => (
-
- -
+
+ -
Freedom
{layout === "freedom" && (
)}
- -
+
-
Spotlight
{layout === "spotlight" && (
diff --git a/src/room/GroupCallLoader.tsx b/src/room/GroupCallLoader.tsx
index 8c13626d..f0ee28f3 100644
--- a/src/room/GroupCallLoader.tsx
+++ b/src/room/GroupCallLoader.tsx
@@ -17,6 +17,7 @@ limitations under the License.
import React, { ReactNode } from "react";
import { MatrixClient } from "matrix-js-sdk/src/client";
import { GroupCall } from "matrix-js-sdk/src/webrtc/groupCall";
+import { useTranslation } from "react-i18next";
import { useLoadGroupCall } from "./useLoadGroupCall";
import { ErrorView, FullScreenView } from "../FullScreenView";
@@ -37,6 +38,7 @@ export function GroupCallLoader({
children,
createPtt,
}: Props): JSX.Element {
+ const { t } = useTranslation();
const { loading, error, groupCall } = useLoadGroupCall(
client,
roomIdOrAlias,
@@ -44,12 +46,12 @@ export function GroupCallLoader({
createPtt
);
- usePageTitle(groupCall ? groupCall.room.name : "Loading...");
+ usePageTitle(groupCall ? groupCall.room.name : t("Loading…"));
if (loading) {
return (
- Loading room...
+ {t("Loading room…")}
);
}
diff --git a/src/room/GroupCallView.tsx b/src/room/GroupCallView.tsx
index b777444e..8bf6a132 100644
--- a/src/room/GroupCallView.tsx
+++ b/src/room/GroupCallView.tsx
@@ -19,6 +19,7 @@ import { useHistory } from "react-router-dom";
import { GroupCall, GroupCallState } from "matrix-js-sdk/src/webrtc/groupCall";
import { MatrixClient } from "matrix-js-sdk/src/client";
import { logger } from "matrix-js-sdk/src/logger";
+import { useTranslation } from "react-i18next";
import type { IWidgetApiRequest } from "matrix-widget-api";
import { widget, ElementWidgetActions, JoinCallData } from "../widget";
@@ -81,8 +82,8 @@ export function GroupCallView({
unencryptedEventsFromUsers,
} = useGroupCall(groupCall);
+ const { t } = useTranslation();
const { setAudioInput, setVideoInput } = useMediaHandler();
-
const avatarUrl = useRoomAvatar(groupCall.room);
useEffect(() => {
@@ -240,7 +241,7 @@ export function GroupCallView({
} else if (state === GroupCallState.Entering) {
return (
- Entering room...
+ {t("Entering room…")}
);
} else if (left) {
@@ -257,7 +258,7 @@ export function GroupCallView({
} else if (isEmbedded) {
return (
- Loading room...
+ {t("Loading room…")}
);
} else {
diff --git a/src/room/InCallView.tsx b/src/room/InCallView.tsx
index fd987e6b..03c95fd2 100644
--- a/src/room/InCallView.tsx
+++ b/src/room/InCallView.tsx
@@ -23,6 +23,7 @@ import { RoomMember } from "matrix-js-sdk/src/models/room-member";
import { GroupCall } from "matrix-js-sdk/src/webrtc/groupCall";
import { CallFeed } from "matrix-js-sdk/src/webrtc/callFeed";
import classNames from "classnames";
+import { useTranslation } from "react-i18next";
import type { IWidgetApiRequest } from "matrix-widget-api";
import styles from "./InCallView.module.css";
@@ -112,6 +113,7 @@ export function InCallView({
unencryptedEventsFromUsers,
hideHeader,
}: Props) {
+ const { t } = useTranslation();
usePreventScroll();
const containerRef1 = useRef(null);
const [containerRef2, bounds] = useMeasure({ polyfill: ResizeObserver });
@@ -247,7 +249,7 @@ export function InCallView({
if (items.length === 0) {
return (
-
Waiting for other participants...
+
{t("Waiting for other participants…")}
);
}
diff --git a/src/room/InviteModal.tsx b/src/room/InviteModal.tsx
index 7c0e7591..75ea5acb 100644
--- a/src/room/InviteModal.tsx
+++ b/src/room/InviteModal.tsx
@@ -15,6 +15,7 @@ limitations under the License.
*/
import React, { FC } from "react";
+import { useTranslation } from "react-i18next";
import { Modal, ModalContent, ModalProps } from "../Modal";
import { CopyButton } from "../button";
@@ -25,19 +26,23 @@ interface Props extends Omit {
roomIdOrAlias: string;
}
-export const InviteModal: FC = ({ roomIdOrAlias, ...rest }) => (
-
-
- Copy and share this meeting link
-
-
-
-);
+export const InviteModal: FC = ({ roomIdOrAlias, ...rest }) => {
+ const { t } = useTranslation();
+
+ return (
+
+
+ {t("Copy and share this call link")}
+
+
+
+ );
+};
diff --git a/src/room/LobbyView.tsx b/src/room/LobbyView.tsx
index 72a0c040..b246d0c0 100644
--- a/src/room/LobbyView.tsx
+++ b/src/room/LobbyView.tsx
@@ -19,6 +19,7 @@ import { GroupCall, GroupCallState } from "matrix-js-sdk/src/webrtc/groupCall";
import { MatrixClient } from "matrix-js-sdk/src/client";
import { PressEvent } from "@react-types/shared";
import { CallFeed } from "matrix-js-sdk/src/webrtc/callFeed";
+import { useTranslation } from "react-i18next";
import styles from "./LobbyView.module.css";
import { Button, CopyButton } from "../button";
@@ -66,6 +67,7 @@ export function LobbyView({
isEmbedded,
hideHeader,
}: Props) {
+ const { t } = useTranslation();
const { stream } = useCallFeed(localCallFeed);
const {
audioInput,
@@ -142,15 +144,15 @@ export function LobbyView({
variant="secondaryCopy"
value={getRoomUrl(roomIdOrAlias)}
className={styles.copyButton}
- copiedMessage="Call link copied"
+ copiedMessage={t("Call link copied")}
>
- Copy call link and join later
+ {t("Copy call link and join later")}
{!isEmbedded && (
- Take me Home
+ {t("Take me Home")}
)}
diff --git a/src/room/OverflowMenu.tsx b/src/room/OverflowMenu.tsx
index f973f8be..fc726b63 100644
--- a/src/room/OverflowMenu.tsx
+++ b/src/room/OverflowMenu.tsx
@@ -18,6 +18,7 @@ import React, { useCallback } from "react";
import { Item } from "@react-stately/collections";
import { GroupCall } from "matrix-js-sdk/src/webrtc/groupCall";
import { OverlayTriggerState } from "@react-stately/overlays";
+import { useTranslation } from "react-i18next";
import { Button } from "../button";
import { Menu } from "../Menu";
@@ -31,6 +32,7 @@ import { SettingsModal } from "../settings/SettingsModal";
import { InviteModal } from "./InviteModal";
import { TooltipTrigger } from "../Tooltip";
import { FeedbackModal } from "./FeedbackModal";
+
interface Props {
roomIdOrAlias: string;
inCall: boolean;
@@ -42,6 +44,7 @@ interface Props {
onClose: () => void;
};
}
+
export function OverflowMenu({
roomIdOrAlias,
inCall,
@@ -50,6 +53,8 @@ export function OverflowMenu({
feedbackModalState,
feedbackModalProps,
}: Props) {
+ const { t } = useTranslation();
+
const {
modalState: inviteModalState,
modalProps: inviteModalProps,
@@ -90,29 +95,31 @@ export function OverflowMenu({
[feedbackModalState, inviteModalState, settingsModalState]
);
+ const tooltip = useCallback(() => t("More"), [t]);
+
return (
<>
- "More"} placement="top">
+
{(props: JSX.IntrinsicAttributes) => (
-
+
{showInvite && (
- -
+
-
- Invite people
+ {t("Invite people")}
)}
- -
+
-
- Settings
+ {t("Settings")}
- -
+
-
- Submit Feedback
+ {t("Submit feedback")}
)}
diff --git a/src/room/PTTCallView.tsx b/src/room/PTTCallView.tsx
index eed22770..922bd148 100644
--- a/src/room/PTTCallView.tsx
+++ b/src/room/PTTCallView.tsx
@@ -17,10 +17,12 @@ limitations under the License.
import React, { useEffect } from "react";
import useMeasure from "react-use-measure";
import { ResizeObserver } from "@juggle/resize-observer";
+import i18n from "i18next";
import { MatrixClient } from "matrix-js-sdk/src/client";
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
import { GroupCall } from "matrix-js-sdk/src/webrtc/groupCall";
import { CallFeed } from "matrix-js-sdk/src/webrtc/callFeed";
+import { useTranslation } from "react-i18next";
import { useDelayedState } from "../useDelayedState";
import { useModalTriggerState } from "../Modal";
@@ -50,40 +52,45 @@ function getPromptText(
talkOverEnabled: boolean,
activeSpeakerUserId: string,
activeSpeakerDisplayName: string,
- connected: boolean
+ connected: boolean,
+ t: typeof i18n.t
): string {
- if (!connected) return "Connection lost";
+ if (!connected) return t("Connection lost");
const isTouchScreen = Boolean(window.ontouchstart !== undefined);
if (networkWaiting) {
- return "Waiting for network";
+ return t("Waiting for network");
}
if (showTalkOverError) {
- return "You can't talk at the same time";
+ return t("You can't talk at the same time");
}
if (pttButtonHeld && activeSpeakerIsLocalUser) {
if (isTouchScreen) {
- return "Release to stop";
+ return t("Release to stop");
} else {
- return "Release spacebar key to stop";
+ return t("Release spacebar key to stop");
}
}
if (talkOverEnabled && activeSpeakerUserId && !activeSpeakerIsLocalUser) {
if (isTouchScreen) {
- return `Press and hold to talk over ${activeSpeakerDisplayName}`;
+ return t("Press and hold to talk over {{name}}", {
+ name: activeSpeakerDisplayName,
+ });
} else {
- return `Press and hold spacebar to talk over ${activeSpeakerDisplayName}`;
+ return t("Press and hold spacebar to talk over {{name}}", {
+ name: activeSpeakerDisplayName,
+ });
}
}
if (isTouchScreen) {
- return "Press and hold to talk";
+ return t("Press and hold to talk");
} else {
- return "Press and hold spacebar to talk";
+ return t("Press and hold spacebar to talk");
}
}
@@ -112,6 +119,7 @@ export const PTTCallView: React.FC = ({
isEmbedded,
hideHeader,
}) => {
+ const { t } = useTranslation();
const { modalState: inviteModalState, modalProps: inviteModalProps } =
useModalTriggerState();
const { modalState: feedbackModalState, modalProps: feedbackModalProps } =
@@ -195,9 +203,11 @@ export const PTTCallView: React.FC = ({
{showControls && (
<>
-
{`${participants.length} ${
- participants.length > 1 ? "people" : "person"
- } connected`}
+
+ {t("{{count}} people connected", {
+ count: participants.length,
+ })}
+
= ({
)}
{activeSpeakerIsLocalUser
- ? "Talking..."
- : `${activeSpeakerDisplayName} is talking...`}
+ ? t("Talking…")
+ : t("{{name}} is talking…", {
+ name: activeSpeakerDisplayName,
+ })}
@@ -263,7 +275,8 @@ export const PTTCallView: React.FC = ({
talkOverEnabled,
activeSpeakerUserId,
activeSpeakerDisplayName,
- connected
+ connected,
+ t
)}
)}
@@ -278,7 +291,7 @@ export const PTTCallView: React.FC = ({
)}
diff --git a/src/room/RageshakeRequestModal.tsx b/src/room/RageshakeRequestModal.tsx
index 59b74b3a..d1220b5d 100644
--- a/src/room/RageshakeRequestModal.tsx
+++ b/src/room/RageshakeRequestModal.tsx
@@ -15,6 +15,7 @@ limitations under the License.
*/
import React, { FC, useEffect } from "react";
+import { useTranslation } from "react-i18next";
import { Modal, ModalContent, ModalProps } from "../Modal";
import { Button } from "../button";
@@ -33,6 +34,7 @@ export const RageshakeRequestModal: FC = ({
roomIdOrAlias,
...rest
}) => {
+ const { t } = useTranslation();
const { submitRageshake, sending, sent, error } = useSubmitRageshake();
useEffect(() => {
@@ -42,11 +44,12 @@ export const RageshakeRequestModal: FC = ({
}, [sent, rest]);
return (
-
+
- Another user on this call is having an issue. In order to better
- diagnose these issues we'd like to collect a debug log.
+ {t(
+ "Another user on this call is having an issue. In order to better diagnose these issues we'd like to collect a debug log."
+ )}
= ({
}
disabled={sending}
>
- {sending ? "Sending debug log..." : "Send debug log"}
+ {sending ? t("Sending debug log…") : t("Send debug log")}
{error && (
- {error.message}
+
)}
diff --git a/src/room/RoomAuthView.tsx b/src/room/RoomAuthView.tsx
index ff2e8f04..d38f05b7 100644
--- a/src/room/RoomAuthView.tsx
+++ b/src/room/RoomAuthView.tsx
@@ -16,6 +16,7 @@ limitations under the License.
import React, { useCallback, useState } from "react";
import { useLocation } from "react-router-dom";
+import { Trans, useTranslation } from "react-i18next";
import styles from "./RoomAuthView.module.css";
import { Button } from "../button";
@@ -50,6 +51,7 @@ export function RoomAuthView() {
[registerPasswordlessUser]
);
+ const { t } = useTranslation();
const location = useLocation();
return (
@@ -64,42 +66,46 @@ export function RoomAuthView() {
- Join Call
+ {t("Join call")}
- {"Not registered yet? "}
-
- Create an account
-
+
+ {"Not registered yet? "}
+
+ Create an account
+
+
>
diff --git a/src/room/RoomPage.tsx b/src/room/RoomPage.tsx
index 1adff571..e8b3fa9a 100644
--- a/src/room/RoomPage.tsx
+++ b/src/room/RoomPage.tsx
@@ -15,6 +15,7 @@ limitations under the License.
*/
import React, { FC, useEffect, useState, useCallback } from "react";
+import { useTranslation } from "react-i18next";
import type { GroupCall } from "matrix-js-sdk/src/webrtc/groupCall";
import { useClient } from "../ClientContext";
@@ -22,11 +23,13 @@ import { ErrorView, LoadingView } from "../FullScreenView";
import { RoomAuthView } from "./RoomAuthView";
import { GroupCallLoader } from "./GroupCallLoader";
import { GroupCallView } from "./GroupCallView";
-import { useRoomParams } from "./useRoomParams";
+import { useUrlParams } from "../UrlParams";
import { MediaHandlerProvider } from "../settings/useMediaHandler";
import { useRegisterPasswordlessUser } from "../auth/useRegisterPasswordlessUser";
+import { translatedError } from "../TranslatedError";
export const RoomPage: FC = () => {
+ const { t } = useTranslation();
const { loading, isAuthenticated, error, client, isPasswordlessUser } =
useClient();
@@ -39,9 +42,9 @@ export const RoomPage: FC = () => {
hideHeader,
isPtt,
displayName,
- } = useRoomParams();
+ } = useUrlParams();
const roomIdOrAlias = roomId ?? roomAlias;
- if (!roomIdOrAlias) throw new Error("No room specified");
+ if (!roomIdOrAlias) throw translatedError("No room specified", t);
const { registerPasswordlessUser } = useRegisterPasswordlessUser();
const [isRegistering, setIsRegistering] = useState(false);
diff --git a/src/room/VideoPreview.tsx b/src/room/VideoPreview.tsx
index 21b559d1..07aa95e8 100644
--- a/src/room/VideoPreview.tsx
+++ b/src/room/VideoPreview.tsx
@@ -19,6 +19,7 @@ import useMeasure from "react-use-measure";
import { ResizeObserver } from "@juggle/resize-observer";
import { GroupCallState } from "matrix-js-sdk/src/webrtc/groupCall";
import { MatrixClient } from "matrix-js-sdk/src/client";
+import { useTranslation } from "react-i18next";
import { MicButton, VideoButton } from "../button";
import { useMediaStream } from "../video-grid/useMediaStream";
@@ -40,6 +41,7 @@ interface Props {
audioOutput: string;
stream: MediaStream;
}
+
export function VideoPreview({
client,
state,
@@ -51,6 +53,7 @@ export function VideoPreview({
audioOutput,
stream,
}: Props) {
+ const { t } = useTranslation();
const videoRef = useMediaStream(stream, audioOutput, true);
const { displayName, avatarUrl } = useProfile(client);
const [previewRef, previewBounds] = useMeasure({ polyfill: ResizeObserver });
@@ -64,12 +67,12 @@ export function VideoPreview({
{state === GroupCallState.LocalCallFeedUninitialized && (
- Camera/microphone permissions needed to join the call.
+ {t("Camera/microphone permissions needed to join the call.")}
)}
{state === GroupCallState.InitializingLocalCallFeed && (
- Accept camera/microphone permissions to join the call.
+ {t("Accept camera/microphone permissions to join the call.")}
)}
{state === GroupCallState.LocalCallFeedInitialized && (
diff --git a/src/room/useGroupCall.ts b/src/room/useGroupCall.ts
index 7f3232ae..5221f803 100644
--- a/src/room/useGroupCall.ts
+++ b/src/room/useGroupCall.ts
@@ -26,8 +26,10 @@ import {
import { MatrixCall } from "matrix-js-sdk/src/webrtc/call";
import { CallFeed } from "matrix-js-sdk/src/webrtc/callFeed";
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
+import { useTranslation } from "react-i18next";
import { usePageUnload } from "./usePageUnload";
+import { TranslatedError, translatedError } from "../TranslatedError";
export interface UseGroupCallReturnType {
state: GroupCallState;
@@ -37,7 +39,7 @@ export interface UseGroupCallReturnType {
userMediaFeeds: CallFeed[];
microphoneMuted: boolean;
localVideoMuted: boolean;
- error: Error;
+ error: TranslatedError | null;
initLocalCallFeed: () => void;
enter: () => void;
leave: () => void;
@@ -60,7 +62,7 @@ interface State {
localCallFeed: CallFeed;
activeSpeaker: string;
userMediaFeeds: CallFeed[];
- error: Error;
+ error: TranslatedError | null;
microphoneMuted: boolean;
localVideoMuted: boolean;
screenshareFeeds: CallFeed[];
@@ -309,15 +311,18 @@ export function useGroupCall(groupCall: GroupCall): UseGroupCallReturnType {
});
}, [groupCall]);
+ const { t } = useTranslation();
+
useEffect(() => {
if (window.RTCPeerConnection === undefined) {
- const error = new Error(
- "WebRTC is not supported or is being blocked in this browser."
+ const error = translatedError(
+ "WebRTC is not supported or is being blocked in this browser.",
+ t
);
console.error(error);
updateState({ error });
}
- }, []);
+ }, [t]);
return {
state,
diff --git a/src/room/useLoadGroupCall.ts b/src/room/useLoadGroupCall.ts
index cd09ca67..5b3d08c9 100644
--- a/src/room/useLoadGroupCall.ts
+++ b/src/room/useLoadGroupCall.ts
@@ -24,10 +24,12 @@ import { GroupCallEventHandlerEvent } from "matrix-js-sdk/src/webrtc/groupCallEv
import { logger } from "matrix-js-sdk/src/logger";
import { ClientEvent, MatrixClient } from "matrix-js-sdk/src/client";
import { SyncState } from "matrix-js-sdk/src/sync";
+import { useTranslation } from "react-i18next";
import type { Room } from "matrix-js-sdk/src/models/room";
import type { GroupCall } from "matrix-js-sdk/src/webrtc/groupCall";
import { isLocalRoomId, createRoom, roomNameFromRoomId } from "../matrix-utils";
+import { translatedError } from "../TranslatedError";
export interface GroupCallLoadState {
loading: boolean;
@@ -41,6 +43,7 @@ export const useLoadGroupCall = (
viaServers: string[],
createPtt: boolean
): GroupCallLoadState => {
+ const { t } = useTranslation();
const [state, setState] = useState({ loading: true });
useEffect(() => {
@@ -122,7 +125,7 @@ export const useLoadGroupCall = (
const timeout = setTimeout(() => {
client.off(GroupCallEventHandlerEvent.Incoming, onGroupCallIncoming);
- reject(new Error("Fetching group call timed out."));
+ reject(translatedError("Fetching group call timed out.", t));
}, 30000);
});
};
@@ -153,7 +156,7 @@ export const useLoadGroupCall = (
.catch((error) =>
setState((prevState) => ({ ...prevState, loading: false, error }))
);
- }, [client, roomIdOrAlias, viaServers, createPtt]);
+ }, [client, roomIdOrAlias, viaServers, createPtt, t]);
return state;
};
diff --git a/src/settings/SettingsModal.tsx b/src/settings/SettingsModal.tsx
index 53764e39..b2c98b82 100644
--- a/src/settings/SettingsModal.tsx
+++ b/src/settings/SettingsModal.tsx
@@ -16,6 +16,7 @@ limitations under the License.
import React from "react";
import { Item } from "@react-stately/collections";
+import { useTranslation } from "react-i18next";
import { Modal } from "../Modal";
import styles from "./SettingsModal.module.css";
@@ -37,6 +38,7 @@ interface Props {
}
export const SettingsModal = (props: Props) => {
+ const { t } = useTranslation();
const {
audioInput,
audioInputs,
@@ -56,7 +58,7 @@ export const SettingsModal = (props: Props) => {
return (
{
title={
<>
- Audio
+ {t("Audio")}
>
}
>
@@ -80,13 +82,13 @@ export const SettingsModal = (props: Props) => {
-
{!!label && label.trim().length > 0
? label
- : `Microphone ${index + 1}`}
+ : t("Microphone {{n}}", { n: index + 1 })}
))}
{audioOutputs.length > 0 && (
@@ -94,7 +96,7 @@ export const SettingsModal = (props: Props) => {
-
{!!label && label.trim().length > 0
? label
- : `Speaker ${index + 1}`}
+ : t("Speaker {{n}}", { n: index + 1 })}
))}
@@ -102,10 +104,12 @@ export const SettingsModal = (props: Props) => {
) =>
setSpatialAudio(event.target.checked)
}
@@ -116,12 +120,12 @@ export const SettingsModal = (props: Props) => {
title={
<>
- Video
+ {t("Video")}
>
}
>
@@ -129,7 +133,7 @@ export const SettingsModal = (props: Props) => {
-
{!!label && label.trim().length > 0
? label
- : `Camera ${index + 1}`}
+ : t("Camera {{n}}", { n: index + 1 })}
))}
@@ -138,20 +142,22 @@ export const SettingsModal = (props: Props) => {
title={
<>
- Developer
+ {t("Developer")}
>
}
>
- Version: {import.meta.env.VITE_APP_VERSION || "dev"}
+ {t("Version: {{version}}", {
+ version: import.meta.env.VITE_APP_VERSION || "dev",
+ })}
) =>
@@ -160,7 +166,9 @@ export const SettingsModal = (props: Props) => {
/>
- Download Debug Logs
+
+ {t("Download debug logs")}
+
diff --git a/src/video-grid/VideoTile.tsx b/src/video-grid/VideoTile.tsx
index 5bd929cd..901d2453 100644
--- a/src/video-grid/VideoTile.tsx
+++ b/src/video-grid/VideoTile.tsx
@@ -17,6 +17,7 @@ limitations under the License.
import React, { forwardRef } from "react";
import { animated } from "@react-spring/web";
import classNames from "classnames";
+import { useTranslation } from "react-i18next";
import styles from "./VideoTile.module.css";
import { ReactComponent as MicMutedIcon } from "../icons/MicMuted.svg";
@@ -66,6 +67,8 @@ export const VideoTile = forwardRef(
},
ref
) => {
+ const { t } = useTranslation();
+
const toolbarButtons: JSX.Element[] = [];
if (!isLocal) {
toolbarButtons.push(
@@ -111,7 +114,7 @@ export const VideoTile = forwardRef(
{!maximised &&
(screenshare ? (
- {`${name} is presenting`}
+ {t("{{name}} is presenting", { name })}
) : (
diff --git a/src/video-grid/VideoTileSettingsModal.tsx b/src/video-grid/VideoTileSettingsModal.tsx
index fd6a28b1..bc5dd6d0 100644
--- a/src/video-grid/VideoTileSettingsModal.tsx
+++ b/src/video-grid/VideoTileSettingsModal.tsx
@@ -16,6 +16,7 @@ limitations under the License.
import React, { ChangeEvent, useState } from "react";
import { CallFeed } from "matrix-js-sdk/src/webrtc/callFeed";
+import { useTranslation } from "react-i18next";
import { FieldRow } from "../input/Input";
import { Modal } from "../Modal";
@@ -61,10 +62,12 @@ interface Props {
}
export const VideoTileSettingsModal = ({ feed, ...rest }: Props) => {
+ const { t } = useTranslation();
+
return (
{
// We need to do this now rather than later because it has capabilities to
// request, and is responsible for starting the transport (should it be?)
- const { roomId, userId, deviceId } = getRoomParams();
+ const { roomId, userId, deviceId } = getUrlParams();
if (!roomId) throw new Error("Room ID must be supplied");
if (!userId) throw new Error("User ID must be supplied");
if (!deviceId) throw new Error("Device ID must be supplied");
diff --git a/vite.config.js b/vite.config.js
index 83440ca8..3ed9636c 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -29,7 +29,7 @@ export default defineConfig(({ mode }) => {
},
plugins: [
svgrPlugin(),
- htmlTemplate({
+ htmlTemplate.default({
data: {
title: env.VITE_PRODUCT_NAME || "Matrix Video Chat",
},
diff --git a/yarn.lock b/yarn.lock
index 13cdd421..578b60a2 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1094,6 +1094,13 @@
dependencies:
regenerator-runtime "^0.13.4"
+"@babel/runtime@^7.14.5", "@babel/runtime@^7.15.4", "@babel/runtime@^7.17.2", "@babel/runtime@^7.19.0":
+ version "7.19.0"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.19.0.tgz#22b11c037b094d27a8a2504ea4dcff00f50e2259"
+ integrity sha512-eR8Lo9hnDS7tqkO7NsV+mKvCmv5boaXFSZ70DnfhcgiEne8hv9oCEd36Klw74EtizEqLsy4YnW8UWwpBVolHZA==
+ dependencies:
+ regenerator-runtime "^0.13.4"
+
"@babel/template@^7.12.7", "@babel/template@^7.18.6":
version "7.18.6"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.6.tgz#1283f4993e00b929d6e2d3c72fdc9168a2977a31"
@@ -2816,7 +2823,7 @@
dependencies:
"@types/unist" "*"
-"@types/minimatch@*":
+"@types/minimatch@*", "@types/minimatch@^3.0.3":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40"
integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==
@@ -2920,6 +2927,11 @@
resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9"
integrity sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==
+"@types/symlink-or-copy@^1.2.0":
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/@types/symlink-or-copy/-/symlink-or-copy-1.2.0.tgz#4151a81b4052c80bc2becbae09f3a9ec010a9c7a"
+ integrity sha512-Lja2xYuuf2B3knEsga8ShbOdsfNOtzT73GyJmZyY7eGl2+ajOqrs8yM5ze0fsSoYwvA6bw7/Qr7OZ7PEEmYwWg==
+
"@types/tapable@^1", "@types/tapable@^1.0.5":
version "1.0.8"
resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.8.tgz#b94a4391c85666c7b73299fd3ad79d4faa435310"
@@ -3518,6 +3530,13 @@ app-root-dir@^1.0.2:
resolved "https://registry.yarnpkg.com/app-root-dir/-/app-root-dir-1.0.2.tgz#38187ec2dea7577fff033ffcb12172692ff6e118"
integrity sha512-jlpIfsOoNoafl92Sz//64uQHGSyMrD2vYG5d8o2a4qGvyNCvXur7bzIsWtAC/6flI2RYAp3kv8rsfBtaLm7w0g==
+append-buffer@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/append-buffer/-/append-buffer-1.0.2.tgz#d8220cf466081525efea50614f3de6514dfa58f1"
+ integrity sha512-WLbYiXzD3y/ATLZFufV/rZvWdZOs+Z/+5v1rBZ463Jn398pa6kcde27cvozYnBoxXblGZTFfoPpsaEw0orU5BA==
+ dependencies:
+ buffer-equal "^1.0.0"
+
"aproba@^1.0.3 || ^2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc"
@@ -4036,6 +4055,38 @@ braces@^3.0.2, braces@~3.0.2:
dependencies:
fill-range "^7.0.1"
+broccoli-node-api@^1.7.0:
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/broccoli-node-api/-/broccoli-node-api-1.7.0.tgz#391aa6edecd2a42c63c111b4162956b2fa288cb6"
+ integrity sha512-QIqLSVJWJUVOhclmkmypJJH9u9s/aWH4+FH6Q6Ju5l+Io4dtwqdPUNmDfw40o6sxhbZHhqGujDJuHTML1wG8Yw==
+
+broccoli-node-info@^2.1.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/broccoli-node-info/-/broccoli-node-info-2.2.0.tgz#feb01c13020792f429e01d7f7845dc5b3a7932b3"
+ integrity sha512-VabSGRpKIzpmC+r+tJueCE5h8k6vON7EIMMWu6d/FyPdtijwLQ7QvzShEw+m3mHoDzUaj/kiZsDYrS8X2adsBg==
+
+broccoli-output-wrapper@^3.2.5:
+ version "3.2.5"
+ resolved "https://registry.yarnpkg.com/broccoli-output-wrapper/-/broccoli-output-wrapper-3.2.5.tgz#514b17801c92922a2c2f87fd145df2a25a11bc5f"
+ integrity sha512-bQAtwjSrF4Nu0CK0JOy5OZqw9t5U0zzv2555EA/cF8/a8SLDTIetk9UgrtMVw7qKLKdSpOZ2liZNeZZDaKgayw==
+ dependencies:
+ fs-extra "^8.1.0"
+ heimdalljs-logger "^0.1.10"
+ symlink-or-copy "^1.2.0"
+
+broccoli-plugin@^4.0.7:
+ version "4.0.7"
+ resolved "https://registry.yarnpkg.com/broccoli-plugin/-/broccoli-plugin-4.0.7.tgz#dd176a85efe915ed557d913744b181abe05047db"
+ integrity sha512-a4zUsWtA1uns1K7p9rExYVYG99rdKeGRymW0qOCNkvDPHQxVi3yVyJHhQbM3EZwdt2E0mnhr5e0c/bPpJ7p3Wg==
+ dependencies:
+ broccoli-node-api "^1.7.0"
+ broccoli-output-wrapper "^3.2.5"
+ fs-merger "^3.2.1"
+ promise-map-series "^0.3.0"
+ quick-temp "^0.1.8"
+ rimraf "^3.0.2"
+ symlink-or-copy "^1.3.1"
+
brorand@^1.0.1, brorand@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
@@ -4124,6 +4175,11 @@ bs58@^5.0.0:
dependencies:
base-x "^4.0.0"
+buffer-equal@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-1.0.0.tgz#59616b498304d556abd466966b22eeda3eca5fbe"
+ integrity sha512-tcBWO2Dl4e7Asr9hTGcpVrCe+F7DubpmqWCTbj4FHLmjqO2hIaC383acQubWtRJhdceqs5uBHs6Es+Sk//RKiQ==
+
buffer-from@^1.0.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
@@ -4358,6 +4414,31 @@ character-reference-invalid@^1.0.0:
resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz#083329cda0eae272ab3dbbf37e9a382c13af1560"
integrity sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==
+cheerio-select@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/cheerio-select/-/cheerio-select-2.1.0.tgz#4d8673286b8126ca2a8e42740d5e3c4884ae21b4"
+ integrity sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==
+ dependencies:
+ boolbase "^1.0.0"
+ css-select "^5.1.0"
+ css-what "^6.1.0"
+ domelementtype "^2.3.0"
+ domhandler "^5.0.3"
+ domutils "^3.0.1"
+
+cheerio@^1.0.0-rc.2:
+ version "1.0.0-rc.12"
+ resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.12.tgz#788bf7466506b1c6bf5fae51d24a2c4d62e47683"
+ integrity sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==
+ dependencies:
+ cheerio-select "^2.1.0"
+ dom-serializer "^2.0.0"
+ domhandler "^5.0.3"
+ domutils "^3.0.1"
+ htmlparser2 "^8.0.1"
+ parse5 "^7.0.0"
+ parse5-htmlparser2-tree-adapter "^7.0.0"
+
"chokidar@>=3.0.0 <4.0.0", chokidar@^3.4.1, chokidar@^3.4.2:
version "3.5.3"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
@@ -4477,6 +4558,11 @@ cliui@^7.0.2:
strip-ansi "^6.0.0"
wrap-ansi "^7.0.0"
+clone-buffer@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58"
+ integrity sha512-KLLTJWrvwIP+OPfMn0x2PheDEP20RPUcGXj/ERegTgdmPEZylALQldygiqrPPu8P45uNuPs7ckmReLY6v/iA5g==
+
clone-deep@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387"
@@ -4486,11 +4572,30 @@ clone-deep@^4.0.1:
kind-of "^6.0.2"
shallow-clone "^3.0.0"
+clone-stats@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-1.0.0.tgz#b3782dff8bb5474e18b9b6bf0fdfe782f8777680"
+ integrity sha512-au6ydSpg6nsrigcZ4m8Bc9hxjeW+GJ8xh5G3BJCMt4WXe1H10UNaVOamqQTmrx1kjVuxAHIQSNU6hY4Nsn9/ag==
+
clone@^1.0.2:
version "1.0.4"
resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e"
integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==
+clone@^2.1.1, clone@^2.1.2:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f"
+ integrity sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==
+
+cloneable-readable@^1.0.0:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/cloneable-readable/-/cloneable-readable-1.1.3.tgz#120a00cb053bfb63a222e709f9683ea2e11d8cec"
+ integrity sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==
+ dependencies:
+ inherits "^2.0.1"
+ process-nextick-args "^2.0.0"
+ readable-stream "^2.3.5"
+
clsx@^1.1.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12"
@@ -4543,6 +4648,11 @@ color-support@^1.1.2:
resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2"
integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==
+colors@1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78"
+ integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==
+
combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6:
version "1.0.8"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
@@ -4575,6 +4685,11 @@ commander@^6.2.1:
resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c"
integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==
+commander@~9.4.1:
+ version "9.4.1"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-9.4.1.tgz#d1dd8f2ce6faf93147295c0df13c7c21141cfbdd"
+ integrity sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw==
+
common-path-prefix@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/common-path-prefix/-/common-path-prefix-3.0.0.tgz#7d007a7e07c58c4b4d5f433131a19141b29f11e0"
@@ -4625,6 +4740,16 @@ concat-stream@^1.5.0:
readable-stream "^2.2.2"
typedarray "^0.0.6"
+concat-stream@~2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-2.0.0.tgz#414cf5af790a48c60ab9be4527d56d5e41133cb1"
+ integrity sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==
+ dependencies:
+ buffer-from "^1.0.0"
+ inherits "^2.0.3"
+ readable-stream "^3.0.2"
+ typedarray "^0.0.6"
+
console-browserify@^1.1.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336"
@@ -4652,7 +4777,7 @@ content-type@^1.0.4, content-type@~1.0.4:
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
-convert-source-map@^1.6.0, convert-source-map@^1.7.0:
+convert-source-map@^1.5.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0:
version "1.8.0"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369"
integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==
@@ -4799,7 +4924,7 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7:
safe-buffer "^5.0.1"
sha.js "^2.4.8"
-cross-fetch@^3.1.5:
+cross-fetch@3.1.5, cross-fetch@^3.1.5:
version "3.1.5"
resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f"
integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==
@@ -4881,7 +5006,18 @@ css-select@^4.1.3:
domutils "^2.8.0"
nth-check "^2.0.1"
-css-what@^6.0.1:
+css-select@^5.1.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/css-select/-/css-select-5.1.0.tgz#b8ebd6554c3637ccc76688804ad3f6a6fdaea8a6"
+ integrity sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==
+ dependencies:
+ boolbase "^1.0.0"
+ css-what "^6.1.0"
+ domhandler "^5.0.2"
+ domutils "^3.0.1"
+ nth-check "^2.0.1"
+
+css-what@^6.0.1, css-what@^6.1.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4"
integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==
@@ -5435,6 +5571,11 @@ dashdash@^1.12.0:
dependencies:
assert-plus "^1.0.0"
+de-indent@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d"
+ integrity sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==
+
debounce@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.1.tgz#38881d8f4166a5c5848020c11827b834bcb3e0a5"
@@ -5655,6 +5796,15 @@ dom-serializer@^1.0.1:
domhandler "^4.2.0"
entities "^2.0.0"
+dom-serializer@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53"
+ integrity sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==
+ dependencies:
+ domelementtype "^2.3.0"
+ domhandler "^5.0.2"
+ entities "^4.2.0"
+
dom-walk@^0.1.0:
version "0.1.2"
resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84"
@@ -5665,7 +5815,7 @@ domain-browser@^1.1.1:
resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda"
integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==
-domelementtype@^2.0.1, domelementtype@^2.2.0:
+domelementtype@^2.0.1, domelementtype@^2.2.0, domelementtype@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d"
integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==
@@ -5677,6 +5827,13 @@ domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.1:
dependencies:
domelementtype "^2.2.0"
+domhandler@^5.0.1, domhandler@^5.0.2, domhandler@^5.0.3:
+ version "5.0.3"
+ resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31"
+ integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==
+ dependencies:
+ domelementtype "^2.3.0"
+
dompurify@2.3.5:
version "2.3.5"
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.5.tgz#c83ed5a3ae5ce23e52efe654ea052ffb358dd7e3"
@@ -5691,6 +5848,15 @@ domutils@^2.5.2, domutils@^2.8.0:
domelementtype "^2.2.0"
domhandler "^4.2.0"
+domutils@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.0.1.tgz#696b3875238338cb186b6c0612bd4901c89a4f1c"
+ integrity sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==
+ dependencies:
+ dom-serializer "^2.0.0"
+ domelementtype "^2.3.0"
+ domhandler "^5.0.1"
+
dot-case@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751"
@@ -5803,11 +5969,26 @@ enhanced-resolve@^5.9.3:
graceful-fs "^4.2.4"
tapable "^2.2.0"
+ensure-posix-path@^1.1.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/ensure-posix-path/-/ensure-posix-path-1.1.1.tgz#3c62bdb19fa4681544289edb2b382adc029179ce"
+ integrity sha512-VWU0/zXzVbeJNXvME/5EmLuEj2TauvoaTz6aFYK1Z92JCBlDlZ3Gu0tuGR42kpW1754ywTs+QB0g5TP0oj9Zaw==
+
entities@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55"
integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==
+entities@^4.2.0, entities@^4.3.0, entities@^4.4.0:
+ version "4.4.0"
+ resolved "https://registry.yarnpkg.com/entities/-/entities-4.4.0.tgz#97bdaba170339446495e653cfd2db78962900174"
+ integrity sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==
+
+eol@^0.9.1:
+ version "0.9.1"
+ resolved "https://registry.yarnpkg.com/eol/-/eol-0.9.1.tgz#f701912f504074be35c6117a5c4ade49cd547acd"
+ integrity sha512-Ds/TEoZjwggRoz/Q2O7SE3i4Jm66mqTDfmdHdq/7DKVk3bro9Q8h6WdXKdPqFLMoqxrDK5SVRzHVPOS6uuGtrg==
+
errno@^0.1.3, errno@~0.1.7:
version "0.1.8"
resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f"
@@ -6554,6 +6735,11 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
+fast-fifo@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/fast-fifo/-/fast-fifo-1.1.0.tgz#17d1a3646880b9891dfa0c54e69c5fef33cad779"
+ integrity sha512-Kl29QoNbNvn4nhDsLYjyIAaIqaJB6rBx5p3sL9VjaefJ+eMFBWVZiaoguaoZfzEKr5RhAti0UgM8703akGPJ6g==
+
fast-glob@^2.2.6:
version "2.2.7"
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d"
@@ -6768,7 +6954,7 @@ flatted@^3.1.0:
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.6.tgz#022e9218c637f9f3fc9c35ab9c9193f05add60b2"
integrity sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==
-flush-write-stream@^1.0.0:
+flush-write-stream@^1.0.0, flush-write-stream@^1.0.2:
version "1.1.1"
resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8"
integrity sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==
@@ -6896,7 +7082,7 @@ from2@^2.1.0:
inherits "^2.0.1"
readable-stream "^2.0.0"
-fs-extra@^10.1.0:
+fs-extra@^10.0.0, fs-extra@^10.1.0:
version "10.1.0"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf"
integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==
@@ -6905,6 +7091,15 @@ fs-extra@^10.1.0:
jsonfile "^6.0.1"
universalify "^2.0.0"
+fs-extra@^8.0.1, fs-extra@^8.1.0:
+ version "8.1.0"
+ resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
+ integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==
+ dependencies:
+ graceful-fs "^4.2.0"
+ jsonfile "^4.0.0"
+ universalify "^0.1.0"
+
fs-extra@^9.0.0, fs-extra@^9.0.1:
version "9.1.0"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d"
@@ -6915,6 +7110,17 @@ fs-extra@^9.0.0, fs-extra@^9.0.1:
jsonfile "^6.0.1"
universalify "^2.0.0"
+fs-merger@^3.2.1:
+ version "3.2.1"
+ resolved "https://registry.yarnpkg.com/fs-merger/-/fs-merger-3.2.1.tgz#a225b11ae530426138294b8fbb19e82e3d4e0b3b"
+ integrity sha512-AN6sX12liy0JE7C2evclwoo0aCG3PFulLjrTLsJpWh/2mM+DinhpSGqYLbHBBbIW1PLRNcFhJG8Axtz8mQW3ug==
+ dependencies:
+ broccoli-node-api "^1.7.0"
+ broccoli-node-info "^2.1.0"
+ fs-extra "^8.0.1"
+ fs-tree-diff "^2.0.1"
+ walk-sync "^2.2.0"
+
fs-minipass@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb"
@@ -6922,11 +7128,30 @@ fs-minipass@^2.0.0:
dependencies:
minipass "^3.0.0"
+fs-mkdirp-stream@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz#0b7815fc3201c6a69e14db98ce098c16935259eb"
+ integrity sha512-+vSd9frUnapVC2RZYfL3FCB2p3g4TBhaUmrsWlSudsGdnxIuUvBB2QM1VZeBtc49QFwrp+wQLrDs3+xxDgI5gQ==
+ dependencies:
+ graceful-fs "^4.1.11"
+ through2 "^2.0.3"
+
fs-monkey@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.3.tgz#ae3ac92d53bb328efe0e9a1d9541f6ad8d48e2d3"
integrity sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==
+fs-tree-diff@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/fs-tree-diff/-/fs-tree-diff-2.0.1.tgz#343e4745ab435ec39ebac5f9059ad919cd034afa"
+ integrity sha512-x+CfAZ/lJHQqwlD64pYM5QxWjzWhSjroaVsr8PW831zOApL55qPibed0c+xebaLWVr2BnHFoHdrwOv8pzt8R5A==
+ dependencies:
+ "@types/symlink-or-copy" "^1.2.0"
+ heimdalljs-logger "^0.1.7"
+ object-assign "^4.1.0"
+ path-posix "^1.0.0"
+ symlink-or-copy "^1.1.8"
+
fs-write-stream-atomic@^1.0.8:
version "1.0.10"
resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9"
@@ -7087,6 +7312,22 @@ glob-promise@^4.2.0:
dependencies:
"@types/glob" "^7.1.3"
+glob-stream@^6.1.0:
+ version "6.1.0"
+ resolved "https://registry.yarnpkg.com/glob-stream/-/glob-stream-6.1.0.tgz#7045c99413b3eb94888d83ab46d0b404cc7bdde4"
+ integrity sha512-uMbLGAP3S2aDOHUDfdoYcdIePUCfysbAd0IAoWVZbeGU/oNQ8asHVSshLDJUPWxfzj8zsCG7/XeHPHTtow0nsw==
+ dependencies:
+ extend "^3.0.0"
+ glob "^7.1.1"
+ glob-parent "^3.1.0"
+ is-negated-glob "^1.0.0"
+ ordered-read-streams "^1.0.0"
+ pumpify "^1.3.5"
+ readable-stream "^2.1.5"
+ remove-trailing-separator "^1.0.1"
+ to-absolute-glob "^2.0.0"
+ unique-stream "^2.0.2"
+
glob-to-regexp@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab"
@@ -7097,7 +7338,7 @@ glob-to-regexp@^0.4.1:
resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e"
integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==
-glob@^7.0.0, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.2.0:
+glob@^7.0.0, glob@^7.1.1, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.2.0:
version "7.2.3"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
@@ -7162,7 +7403,7 @@ globby@^9.2.0:
pify "^4.0.1"
slash "^2.0.0"
-graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.9:
+graceful-fs@^4.0.0, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.9:
version "4.2.10"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c"
integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==
@@ -7174,6 +7415,13 @@ graphlib@^2.1.8:
dependencies:
lodash "^4.17.15"
+gulp-sort@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/gulp-sort/-/gulp-sort-2.0.0.tgz#c6762a2f1f0de0a3fc595a21599d3fac8dba1aca"
+ integrity sha512-MyTel3FXOdh1qhw1yKhpimQrAmur9q1X0ZigLmCOxouQD+BD3za9/89O+HfbgBQvvh4igEbp0/PUWO+VqGYG1g==
+ dependencies:
+ through2 "^2.0.1"
+
handlebars@^4.7.7:
version "4.7.7"
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1"
@@ -7373,6 +7621,21 @@ he@^1.2.0:
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
+heimdalljs-logger@^0.1.10, heimdalljs-logger@^0.1.7:
+ version "0.1.10"
+ resolved "https://registry.yarnpkg.com/heimdalljs-logger/-/heimdalljs-logger-0.1.10.tgz#90cad58aabb1590a3c7e640ddc6a4cd3a43faaf7"
+ integrity sha512-pO++cJbhIufVI/fmB/u2Yty3KJD0TqNPecehFae0/eps0hkZ3b4Zc/PezUMOpYuHFQbA7FxHZxa305EhmjLj4g==
+ dependencies:
+ debug "^2.2.0"
+ heimdalljs "^0.2.6"
+
+heimdalljs@^0.2.6:
+ version "0.2.6"
+ resolved "https://registry.yarnpkg.com/heimdalljs/-/heimdalljs-0.2.6.tgz#b0eebabc412813aeb9542f9cc622cb58dbdcd9fe"
+ integrity sha512-o9bd30+5vLBvBtzCPwwGqpry2+n0Hi6H1+qwt6y+0kwRHGGF8TFIhJPmnuM0xO97zaKrDZMwO/V56fAnn8m/tA==
+ dependencies:
+ rsvp "~3.2.1"
+
highlight.js@^10.4.1, highlight.js@~10.7.0:
version "10.7.3"
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531"
@@ -7441,6 +7704,13 @@ html-minifier-terser@^5.0.1:
relateurl "^0.2.7"
terser "^4.6.3"
+html-parse-stringify@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz#dfc1017347ce9f77c8141a507f233040c59c55d2"
+ integrity sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==
+ dependencies:
+ void-elements "3.1.0"
+
html-tags@^3.1.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.2.0.tgz#dbb3518d20b726524e4dd43de397eb0a95726961"
@@ -7476,6 +7746,16 @@ htmlparser2@^6.1.0:
domutils "^2.5.2"
entities "^2.0.0"
+htmlparser2@^8.0.1:
+ version "8.0.1"
+ resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-8.0.1.tgz#abaa985474fcefe269bc761a779b544d7196d010"
+ integrity sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==
+ dependencies:
+ domelementtype "^2.3.0"
+ domhandler "^5.0.2"
+ domutils "^3.0.1"
+ entities "^4.3.0"
+
http-errors@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3"
@@ -7511,6 +7791,51 @@ human-signals@^2.1.0:
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0"
integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==
+i18next-browser-languagedetector@^6.1.8:
+ version "6.1.8"
+ resolved "https://registry.yarnpkg.com/i18next-browser-languagedetector/-/i18next-browser-languagedetector-6.1.8.tgz#8e9c61b32a4dfe9b959b38bc9d2a8b95f799b27c"
+ integrity sha512-Svm+MduCElO0Meqpj1kJAriTC6OhI41VhlT/A0UPjGoPZBhAHIaGE5EfsHlTpgdH09UVX7rcc72pSDDBeKSQQA==
+ dependencies:
+ "@babel/runtime" "^7.19.0"
+
+i18next-http-backend@^1.4.4:
+ version "1.4.4"
+ resolved "https://registry.yarnpkg.com/i18next-http-backend/-/i18next-http-backend-1.4.4.tgz#9f372f704975a5e96ce74e4247698bcbd00d148f"
+ integrity sha512-M4gLPe6JKZ2p1UmE6t4rzWV/sAxgrLThW7ztXAsTpFwFqXoyzhTzX8eYxVv9KjpCQh4K9nwxnEjEi+74C4Thbg==
+ dependencies:
+ cross-fetch "3.1.5"
+
+i18next-parser@^6.6.0:
+ version "6.6.0"
+ resolved "https://registry.yarnpkg.com/i18next-parser/-/i18next-parser-6.6.0.tgz#bb47e1a85369ff6fe8144b7bb03fe505374ecac2"
+ integrity sha512-yA3W6PL+7epCyUFTpUDdztKArfpeGMWRUOnB/4FZRodfXkjCIBcBg728h6b/lrBTbva4OlFjVgv1kCXbvZVRWQ==
+ dependencies:
+ "@babel/runtime" "^7.15.4"
+ broccoli-plugin "^4.0.7"
+ cheerio "^1.0.0-rc.2"
+ colors "1.4.0"
+ commander "~9.4.1"
+ concat-stream "~2.0.0"
+ eol "^0.9.1"
+ fs-extra "^10.0.0"
+ gulp-sort "^2.0.0"
+ i18next "^21.2.0"
+ js-yaml "4.1.0"
+ rsvp "^4.8.2"
+ sort-keys "^5.0.0"
+ through2 "~4.0.2"
+ typescript "^4.2.4"
+ vinyl "~3.0.0"
+ vinyl-fs "^3.0.2"
+ vue-template-compiler "^2.6.11"
+
+i18next@^21.10.0, i18next@^21.2.0:
+ version "21.10.0"
+ resolved "https://registry.yarnpkg.com/i18next/-/i18next-21.10.0.tgz#85429af55fdca4858345d0e16b584ec29520197d"
+ integrity sha512-YeuIBmFsGjUfO3qBmMOc0rQaun4mIpGKET5WDwvu8lU7gvwpcariZLNtL0Fzj+zazcHUrlXHiptcFhBMFaxzfg==
+ dependencies:
+ "@babel/runtime" "^7.17.2"
+
iconv-lite@0.4, iconv-lite@0.4.24:
version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
@@ -7659,6 +7984,14 @@ ipaddr.js@1.9.1:
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3"
integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==
+is-absolute@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-absolute/-/is-absolute-1.0.0.tgz#395e1ae84b11f26ad1795e73c17378e48a301576"
+ integrity sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==
+ dependencies:
+ is-relative "^1.0.0"
+ is-windows "^1.0.1"
+
is-accessor-descriptor@^0.1.6:
version "0.1.6"
resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6"
@@ -7860,6 +8193,11 @@ is-map@^2.0.2:
resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127"
integrity sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==
+is-negated-glob@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-negated-glob/-/is-negated-glob-1.0.0.tgz#6910bca5da8c95e784b5751b976cf5a10fee36d2"
+ integrity sha512-czXVVn/QEmgvej1f50BZ648vUI+em0xqMq2Sn+QncCLN4zj1UAxlT+kw/6ggQTOaZPd1HqKQGEqbpQVtJucWug==
+
is-negative-zero@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150"
@@ -7889,6 +8227,11 @@ is-plain-obj@^2.0.0:
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287"
integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==
+is-plain-obj@^4.0.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz#d65025edec3657ce032fd7db63c97883eaed71f0"
+ integrity sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==
+
is-plain-object@5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344"
@@ -7909,6 +8252,13 @@ is-regex@^1.1.2, is-regex@^1.1.4:
call-bind "^1.0.2"
has-tostringtag "^1.0.0"
+is-relative@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-1.0.0.tgz#a1bb6935ce8c5dba1e8b9754b9b2dcc020e2260d"
+ integrity sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==
+ dependencies:
+ is-unc-path "^1.0.0"
+
is-set@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.2.tgz#90755fa4c2562dc1c5d4024760d6119b94ca18ec"
@@ -7945,16 +8295,28 @@ is-typedarray@~1.0.0:
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==
+is-unc-path@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-unc-path/-/is-unc-path-1.0.0.tgz#d731e8898ed090a12c352ad2eaed5095ad322c9d"
+ integrity sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==
+ dependencies:
+ unc-path-regex "^0.1.2"
+
is-unicode-supported@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7"
integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==
-is-utf8@^0.2.0:
+is-utf8@^0.2.0, is-utf8@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72"
integrity sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==
+is-valid-glob@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-valid-glob/-/is-valid-glob-1.0.0.tgz#29bf3eff701be2d4d315dbacc39bc39fe8f601aa"
+ integrity sha512-AhiROmoEFDSsjx8hW+5sGwgKVIORcXnrlAx/R0ZSeaPw70Vw0CqkGBBhHGL58Uox2eXnU1AnvXJl1XlyedO5bA==
+
is-weakref@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2"
@@ -7967,7 +8329,7 @@ is-whitespace-character@^1.0.0:
resolved "https://registry.yarnpkg.com/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz#0858edd94a95594c7c9dd0b5c174ec6e45ee4aa7"
integrity sha512-SDweEzfIZM0SJV0EUga669UTKlmL0Pq8Lno0QDQsPnvECB3IM2aP0gdx5TrU0A01MAPfViaZiI2V1QMZLaKK5w==
-is-windows@^1.0.2:
+is-windows@^1.0.1, is-windows@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d"
integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==
@@ -8102,7 +8464,7 @@ js-string-escape@^1.0.1:
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
-js-yaml@^4.1.0:
+js-yaml@4.1.0, js-yaml@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
@@ -8166,6 +8528,13 @@ json5@^2.1.2, json5@^2.1.3, json5@^2.2.1:
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c"
integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==
+jsonfile@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
+ integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==
+ optionalDependencies:
+ graceful-fs "^4.1.6"
+
jsonfile@^6.0.1:
version "6.1.0"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae"
@@ -8260,6 +8629,20 @@ lazy-universal-dotenv@^3.0.1:
dotenv "^8.0.0"
dotenv-expand "^5.1.0"
+lazystream@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.1.tgz#494c831062f1f9408251ec44db1cba29242a2638"
+ integrity sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==
+ dependencies:
+ readable-stream "^2.0.5"
+
+lead@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/lead/-/lead-1.0.0.tgz#6f14f99a37be3a9dd784f5495690e5903466ee42"
+ integrity sha512-IpSVCk9AYvLHo5ctcIXxOBpMWUe+4TKN3VPWAKUbJikkmsGp0VrSM8IttVc32D6J4WUsiPE6aEFRNmIoF/gdow==
+ dependencies:
+ flush-write-stream "^1.0.2"
+
levn@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade"
@@ -8479,6 +8862,14 @@ markdown-escapes@^1.0.0:
resolved "https://registry.yarnpkg.com/markdown-escapes/-/markdown-escapes-1.0.4.tgz#c95415ef451499d7602b91095f3c8e8975f78535"
integrity sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg==
+matcher-collection@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/matcher-collection/-/matcher-collection-2.0.1.tgz#90be1a4cf58d6f2949864f65bb3b0f3e41303b29"
+ integrity sha512-daE62nS2ZQsDg9raM0IlZzLmI2u+7ZapXBwdoeBUKAYERPDDIc0qNqA8E0Rp2D+gspKR7BgIFP52GeujaGXWeQ==
+ dependencies:
+ "@types/minimatch" "^3.0.3"
+ minimatch "^3.0.2"
+
matrix-events-sdk@^0.0.1-beta.7:
version "0.0.1-beta.7"
resolved "https://registry.yarnpkg.com/matrix-events-sdk/-/matrix-events-sdk-0.0.1-beta.7.tgz#5ffe45eba1f67cc8d7c2377736c728b322524934"
@@ -8820,6 +9211,11 @@ mkdirp@^1.0.3, mkdirp@^1.0.4:
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
+mktemp@~0.4.0:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/mktemp/-/mktemp-0.4.0.tgz#6d0515611c8a8c84e484aa2000129b98e981ff0b"
+ integrity sha512-IXnMcJ6ZyTuhRmJSjzvHSRhlVPiN9Jwc6e59V0bEJ0ba6OBeX2L0E+mRN1QseeOF4mM+F1Rit6Nh7o+rl2Yn/A==
+
moment-mini@^2.24.0:
version "2.24.0"
resolved "https://registry.yarnpkg.com/moment-mini/-/moment-mini-2.24.0.tgz#fa68d98f7fe93ae65bf1262f6abb5fb6983d8d18"
@@ -8992,6 +9388,13 @@ normalize.css@^8.0.1:
resolved "https://registry.yarnpkg.com/normalize.css/-/normalize.css-8.0.1.tgz#9b98a208738b9cc2634caacbc42d131c97487bf3"
integrity sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg==
+now-and-later@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/now-and-later/-/now-and-later-2.0.1.tgz#8e579c8685764a7cc02cb680380e94f43ccb1f7c"
+ integrity sha512-KGvQ0cB70AQfg107Xvs/Fbu+dGmZoTRJp2TaPwcwQm3/7PteUyN2BCgk8KBMPGBUXZdVwyWS8fDCGFygBm19UQ==
+ dependencies:
+ once "^1.3.2"
+
npm-run-path@^4.0.0, npm-run-path@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea"
@@ -9057,6 +9460,16 @@ object-visit@^1.0.0:
dependencies:
isobject "^3.0.0"
+object.assign@^4.0.4:
+ version "4.1.4"
+ resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f"
+ integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==
+ dependencies:
+ call-bind "^1.0.2"
+ define-properties "^1.1.4"
+ has-symbols "^1.0.3"
+ object-keys "^1.1.1"
+
object.assign@^4.1.0, object.assign@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940"
@@ -9136,7 +9549,7 @@ on-headers@~1.0.2:
resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f"
integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==
-once@^1.3.0, once@^1.3.1, once@^1.4.0:
+once@^1.3.0, once@^1.3.1, once@^1.3.2, once@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
@@ -9206,6 +9619,13 @@ ora@^5.4.1:
strip-ansi "^6.0.0"
wcwidth "^1.0.1"
+ordered-read-streams@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz#77c0cb37c41525d64166d990ffad7ec6a0e1363e"
+ integrity sha512-Z87aSjx3r5c0ZB7bcJqIgIRX5bxR7A4aSzvIbaxd0oTkWBCOoKfuGHiKj60CHVUgg1Phm5yMZzBdt8XqRs73Mw==
+ dependencies:
+ readable-stream "^2.0.1"
+
os-browserify@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27"
@@ -9409,11 +9829,26 @@ parse-json@^5.0.0:
json-parse-even-better-errors "^2.3.0"
lines-and-columns "^1.1.6"
+parse5-htmlparser2-tree-adapter@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz#23c2cc233bcf09bb7beba8b8a69d46b08c62c2f1"
+ integrity sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==
+ dependencies:
+ domhandler "^5.0.2"
+ parse5 "^7.0.0"
+
parse5@^6.0.0:
version "6.0.1"
resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b"
integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==
+parse5@^7.0.0:
+ version "7.1.1"
+ resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.1.1.tgz#4649f940ccfb95d8754f37f73078ea20afe0c746"
+ integrity sha512-kwpuwzB+px5WUg9pyK0IcK/shltJN5/OVhQagxhCQNtT9Y9QRZqNY2e1cmbu/paRh5LMnz/oVTVLBpjFmMZhSg==
+ dependencies:
+ entities "^4.4.0"
+
parseurl@~1.3.2, parseurl@~1.3.3:
version "1.3.3"
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
@@ -9474,6 +9909,11 @@ path-parse@^1.0.7:
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
+path-posix@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/path-posix/-/path-posix-1.0.0.tgz#06b26113f56beab042545a23bfa88003ccac260f"
+ integrity sha512-1gJ0WpNIiYcQydgg3Ed8KzvIqTsDpNwq+cjBCssvBtuTWjEqY1AW+i+OepiEMqDCzyro9B2sLAe4RBPajMYFiA==
+
path-to-regexp@0.1.7:
version "0.1.7"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
@@ -9961,7 +10401,7 @@ prismjs@~1.27.0:
resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.27.0.tgz#bb6ee3138a0b438a3653dd4d6ce0cc6510a45057"
integrity sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==
-process-nextick-args@~2.0.0:
+process-nextick-args@^2.0.0, process-nextick-args@~2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
@@ -9976,6 +10416,11 @@ promise-inflight@^1.0.1:
resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3"
integrity sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==
+promise-map-series@^0.3.0:
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/promise-map-series/-/promise-map-series-0.3.0.tgz#41873ca3652bb7a042b387d538552da9b576f8a1"
+ integrity sha512-3npG2NGhTc8BWBolLLf8l/92OxMGaRLbqvIh9wjCHhDXNvk4zsxaTaCpiCunW09qWPrN2zeNSNwRLVBrQQtutA==
+
promise.allsettled@^1.0.0:
version "1.0.5"
resolved "https://registry.yarnpkg.com/promise.allsettled/-/promise.allsettled-1.0.5.tgz#2443f3d4b2aa8dfa560f6ac2aa6c4ea999d75f53"
@@ -10074,7 +10519,7 @@ pump@^3.0.0:
end-of-stream "^1.1.0"
once "^1.3.1"
-pumpify@^1.3.3:
+pumpify@^1.3.3, pumpify@^1.3.5:
version "1.5.1"
resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce"
integrity sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==
@@ -10142,6 +10587,20 @@ queue-microtask@^1.2.2:
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
+queue-tick@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/queue-tick/-/queue-tick-1.0.1.tgz#f6f07ac82c1fd60f82e098b417a80e52f1f4c142"
+ integrity sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==
+
+quick-temp@^0.1.8:
+ version "0.1.8"
+ resolved "https://registry.yarnpkg.com/quick-temp/-/quick-temp-0.1.8.tgz#bab02a242ab8fb0dd758a3c9776b32f9a5d94408"
+ integrity sha512-YsmIFfD9j2zaFwJkzI6eMG7y0lQP7YeWzgtFgNl38pGWZBSXJooZbOWwkcRot7Vt0Fg9L23pX0tqWU3VvLDsiA==
+ dependencies:
+ mktemp "~0.4.0"
+ rimraf "^2.5.4"
+ underscore.string "~3.3.4"
+
ramda@^0.28.0:
version "0.28.0"
resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.28.0.tgz#acd785690100337e8b063cab3470019be427cc97"
@@ -10239,6 +10698,14 @@ react-element-to-jsx-string@^14.3.4:
is-plain-object "5.0.0"
react-is "17.0.2"
+react-i18next@^11.18.6:
+ version "11.18.6"
+ resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-11.18.6.tgz#e159c2960c718c1314f1e8fcaa282d1c8b167887"
+ integrity sha512-yHb2F9BiT0lqoQDt8loZ5gWP331GwctHz9tYQ8A2EIEUu+CcEdjBLQWli1USG3RdWQt3W+jqQLg/d4rrQR96LA==
+ dependencies:
+ "@babel/runtime" "^7.14.5"
+ html-parse-stringify "^3.0.1"
+
react-is@17.0.2:
version "17.0.2"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
@@ -10398,7 +10865,7 @@ read-pkg@^5.2.0:
parse-json "^5.0.0"
type-fest "^0.6.0"
-"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6:
+"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6:
version "2.3.7"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
@@ -10411,7 +10878,7 @@ read-pkg@^5.2.0:
string_decoder "~1.1.1"
util-deprecate "~1.0.1"
-readable-stream@^3.4.0, readable-stream@^3.6.0:
+readable-stream@3, readable-stream@^3.0.2, readable-stream@^3.4.0, readable-stream@^3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
@@ -10583,7 +11050,24 @@ remark-squeeze-paragraphs@4.0.0:
dependencies:
mdast-squeeze-paragraphs "^4.0.0"
-remove-trailing-separator@^1.0.1:
+remove-bom-buffer@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz#c2bf1e377520d324f623892e33c10cac2c252b53"
+ integrity sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ==
+ dependencies:
+ is-buffer "^1.1.5"
+ is-utf8 "^0.2.1"
+
+remove-bom-stream@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz#05f1a593f16e42e1fb90ebf59de8e569525f9523"
+ integrity sha512-wigO8/O08XHb8YPzpDDT+QmRANfW6vLqxfaXm1YXhnFf3AkSLyjfG3GEFg4McZkmgL7KvCj5u2KczkvSP6NfHA==
+ dependencies:
+ remove-bom-buffer "^3.0.0"
+ safe-buffer "^5.1.0"
+ through2 "^2.0.3"
+
+remove-trailing-separator@^1.0.1, remove-trailing-separator@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef"
integrity sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==
@@ -10616,6 +11100,16 @@ repeating@^2.0.0:
dependencies:
is-finite "^1.0.0"
+replace-ext@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.1.tgz#2d6d996d04a15855d967443631dd5f77825b016a"
+ integrity sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==
+
+replace-ext@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-2.0.0.tgz#9471c213d22e1bcc26717cd6e50881d88f812b06"
+ integrity sha512-UszKE5KVK6JvyD92nzMn9cDapSk6w/CaFZ96CnmDMUqH9oowfxF/ZjRITD25H4DnOQClLA4/j7jLGXXLVKxAug==
+
request@^2.88.2:
version "2.88.2"
resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3"
@@ -10657,6 +11151,13 @@ resolve-from@^5.0.0:
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69"
integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
+resolve-options@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/resolve-options/-/resolve-options-1.1.0.tgz#32bb9e39c06d67338dc9378c0d6d6074566ad131"
+ integrity sha512-NYDgziiroVeDC29xq7bp/CacZERYsA9bXYd1ZmcJlF3BcrZv5pTb4NG7SjdyKDnXZ84aC4vo2u6sNKIA1LCu/A==
+ dependencies:
+ value-or-function "^3.0.0"
+
resolve-pathname@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-3.0.0.tgz#99d02224d3cf263689becbb393bc560313025dcd"
@@ -10742,6 +11243,16 @@ rollup@^2.59.0:
optionalDependencies:
fsevents "~2.3.2"
+rsvp@^4.8.2:
+ version "4.8.5"
+ resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734"
+ integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==
+
+rsvp@~3.2.1:
+ version "3.2.1"
+ resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-3.2.1.tgz#07cb4a5df25add9e826ebc67dcc9fd89db27d84a"
+ integrity sha512-Rf4YVNYpKjZ6ASAmibcwTNciQ5Co5Ztq6iZPEykHpkoflnD/K5ryE/rHehFsTm4NJj8nKDhbi3eKBWGogmNnkg==
+
run-parallel@^1.1.9:
version "1.2.0"
resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee"
@@ -11049,6 +11560,13 @@ snapdragon@^0.8.1:
source-map-resolve "^0.5.0"
use "^3.1.0"
+sort-keys@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-5.0.0.tgz#5d775f8ae93ecc29bc7312bbf3acac4e36e3c446"
+ integrity sha512-Pdz01AvCAottHTPQGzndktFNdbRA75BgOfeT1hH+AMnJFv8lynkPi42rfeEhpx1saTEI3YNMWxfqu0sFD1G8pw==
+ dependencies:
+ is-plain-obj "^4.0.0"
+
source-list-map@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34"
@@ -11136,6 +11654,11 @@ split-string@^3.0.1, split-string@^3.0.2:
dependencies:
extend-shallow "^3.0.0"
+sprintf-js@^1.1.1:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673"
+ integrity sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==
+
sshpk@^1.7.0:
version "1.17.0"
resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.17.0.tgz#578082d92d4fe612b13007496e543fa0fbcbe4c5"
@@ -11246,6 +11769,14 @@ stream-shift@^1.0.0:
resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d"
integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==
+streamx@^2.12.5:
+ version "2.12.5"
+ resolved "https://registry.yarnpkg.com/streamx/-/streamx-2.12.5.tgz#485d6b6bf74df6f94bc1cc262e67392ed6862dc0"
+ integrity sha512-Y+nkFw57Z5JHT3zLlqFm3GccOy2FeYdUrrqita6Dd8kr/8enPn9GKa8IYf3/DmEKfZl/E2sWoSKUnd4qhonrgg==
+ dependencies:
+ fast-fifo "^1.0.0"
+ queue-tick "^1.0.0"
+
"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
@@ -11430,6 +11961,11 @@ symbol.prototype.description@^1.0.0:
has-symbols "^1.0.2"
object.getownpropertydescriptors "^2.1.2"
+symlink-or-copy@^1.1.8, symlink-or-copy@^1.2.0, symlink-or-copy@^1.3.1:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/symlink-or-copy/-/symlink-or-copy-1.3.1.tgz#9506dd64d8e98fa21dcbf4018d1eab23e77f71fe"
+ integrity sha512-0K91MEXFpBUaywiwSSkmKjnGcasG/rVBXFLJz5DrgGabpYD6N+3yZrfD6uUIfpuTu65DZLHi7N8CizHc07BPZA==
+
synchronous-promise@^2.0.15:
version "2.0.15"
resolved "https://registry.yarnpkg.com/synchronous-promise/-/synchronous-promise-2.0.15.tgz#07ca1822b9de0001f5ff73595f3d08c4f720eb8e"
@@ -11457,6 +11993,13 @@ tar@^6.0.2:
mkdirp "^1.0.3"
yallist "^4.0.0"
+teex@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/teex/-/teex-1.0.1.tgz#b8fa7245ef8e8effa8078281946c85ab780a0b12"
+ integrity sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==
+ dependencies:
+ streamx "^2.12.5"
+
telejson@^6.0.8:
version "6.0.8"
resolved "https://registry.yarnpkg.com/telejson/-/telejson-6.0.8.tgz#1c432db7e7a9212c1fbd941c3e5174ec385148f7"
@@ -11545,7 +12088,15 @@ text-table@^0.2.0:
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==
-through2@^2.0.0:
+through2-filter@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/through2-filter/-/through2-filter-3.0.0.tgz#700e786df2367c2c88cd8aa5be4cf9c1e7831254"
+ integrity sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==
+ dependencies:
+ through2 "~2.0.0"
+ xtend "~4.0.0"
+
+through2@^2.0.0, through2@^2.0.1, through2@^2.0.3, through2@~2.0.0:
version "2.0.5"
resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd"
integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==
@@ -11553,6 +12104,13 @@ through2@^2.0.0:
readable-stream "~2.3.6"
xtend "~4.0.1"
+through2@~4.0.2:
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/through2/-/through2-4.0.2.tgz#a7ce3ac2a7a8b0b966c80e7c49f0484c3b239764"
+ integrity sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==
+ dependencies:
+ readable-stream "3"
+
timers-browserify@^2.0.4:
version "2.0.12"
resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.12.tgz#44a45c11fbf407f34f97bccd1577c652361b00ee"
@@ -11570,6 +12128,14 @@ tiny-warning@^1.0.0, tiny-warning@^1.0.3:
resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754"
integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==
+to-absolute-glob@^2.0.0:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz#1865f43d9e74b0822db9f145b78cff7d0f7c849b"
+ integrity sha512-rtwLUQEwT8ZeKQbyFJyomBRYXyE16U5VKuy0ftxLMK/PZb2fkOsg5r9kHdauuVDbsNdIBoC/HCthpidamQFXYA==
+ dependencies:
+ is-absolute "^1.0.0"
+ is-negated-glob "^1.0.0"
+
to-arraybuffer@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43"
@@ -11612,6 +12178,13 @@ to-regex@^3.0.1, to-regex@^3.0.2:
regex-not "^1.0.2"
safe-regex "^1.1.0"
+to-through@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/to-through/-/to-through-2.0.0.tgz#fc92adaba072647bc0b67d6b03664aa195093af6"
+ integrity sha512-+QIz37Ly7acM4EMdw2PRN389OneM5+d844tirkGp4dPKzI5OE72V9OsbFp+CIYJDahZ41ZV05hNtcPAQUAm9/Q==
+ dependencies:
+ through2 "^2.0.3"
+
toggle-selection@^1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32"
@@ -11761,6 +12334,11 @@ typescript-strict-plugin@^2.0.1:
ora "^5.4.1"
yargs "^16.2.0"
+typescript@^4.2.4:
+ version "4.8.4"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.4.tgz#c464abca159669597be5f96b8943500b238e60e6"
+ integrity sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==
+
typescript@^4.6.4:
version "4.7.4"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235"
@@ -11786,6 +12364,19 @@ unbox-primitive@^1.0.2:
has-symbols "^1.0.3"
which-boxed-primitive "^1.0.2"
+unc-path-regex@^0.1.2:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa"
+ integrity sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg==
+
+underscore.string@~3.3.4:
+ version "3.3.6"
+ resolved "https://registry.yarnpkg.com/underscore.string/-/underscore.string-3.3.6.tgz#ad8cf23d7423cb3b53b898476117588f4e2f9159"
+ integrity sha512-VoC83HWXmCrF6rgkyxS9GHv8W9Q5nhMKho+OadDJGzL2oDYbYEppBaCMH6pFlwLeqj2QS+hhkw2kpXkSdD1JxQ==
+ dependencies:
+ sprintf-js "^1.1.1"
+ util-deprecate "^1.0.2"
+
unfetch@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/unfetch/-/unfetch-4.2.0.tgz#7e21b0ef7d363d8d9af0fb929a5555f6ef97a3be"
@@ -11880,6 +12471,14 @@ unique-slug@^2.0.0:
dependencies:
imurmurhash "^0.1.4"
+unique-stream@^2.0.2:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/unique-stream/-/unique-stream-2.3.1.tgz#c65d110e9a4adf9a6c5948b28053d9a8d04cbeac"
+ integrity sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==
+ dependencies:
+ json-stable-stringify-without-jsonify "^1.0.1"
+ through2-filter "^3.0.0"
+
unist-builder@2.0.3, unist-builder@^2.0.0:
version "2.0.3"
resolved "https://registry.yarnpkg.com/unist-builder/-/unist-builder-2.0.3.tgz#77648711b5d86af0942f334397a33c5e91516436"
@@ -11938,6 +12537,11 @@ unist-util-visit@2.0.3, unist-util-visit@^2.0.0:
unist-util-is "^4.0.0"
unist-util-visit-parents "^3.0.0"
+universalify@^0.1.0:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
+ integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
+
universalify@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717"
@@ -12096,6 +12700,11 @@ value-equal@^1.0.1:
resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-1.0.1.tgz#1e0b794c734c5c0cade179c437d356d931a34d6c"
integrity sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==
+value-or-function@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/value-or-function/-/value-or-function-3.0.0.tgz#1c243a50b595c1be54a754bfece8563b9ff8d813"
+ integrity sha512-jdBB2FrWvQC/pnPtIqcLsMaQgjhdb6B7tk1MMyTKapox+tQZbdRP4uLxu/JY0t7fbfDCUMnuelzEYv5GsxHhdg==
+
vary@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
@@ -12133,6 +12742,65 @@ vfile@^4.0.0:
unist-util-stringify-position "^2.0.0"
vfile-message "^2.0.0"
+vinyl-fs@^3.0.2:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/vinyl-fs/-/vinyl-fs-3.0.3.tgz#c85849405f67428feabbbd5c5dbdd64f47d31bc7"
+ integrity sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng==
+ dependencies:
+ fs-mkdirp-stream "^1.0.0"
+ glob-stream "^6.1.0"
+ graceful-fs "^4.0.0"
+ is-valid-glob "^1.0.0"
+ lazystream "^1.0.0"
+ lead "^1.0.0"
+ object.assign "^4.0.4"
+ pumpify "^1.3.5"
+ readable-stream "^2.3.3"
+ remove-bom-buffer "^3.0.0"
+ remove-bom-stream "^1.2.0"
+ resolve-options "^1.1.0"
+ through2 "^2.0.0"
+ to-through "^2.0.0"
+ value-or-function "^3.0.0"
+ vinyl "^2.0.0"
+ vinyl-sourcemap "^1.1.0"
+
+vinyl-sourcemap@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz#92a800593a38703a8cdb11d8b300ad4be63b3e16"
+ integrity sha512-NiibMgt6VJGJmyw7vtzhctDcfKch4e4n9TBeoWlirb7FMg9/1Ov9k+A5ZRAtywBpRPiyECvQRQllYM8dECegVA==
+ dependencies:
+ append-buffer "^1.0.2"
+ convert-source-map "^1.5.0"
+ graceful-fs "^4.1.6"
+ normalize-path "^2.1.1"
+ now-and-later "^2.0.0"
+ remove-bom-buffer "^3.0.0"
+ vinyl "^2.0.0"
+
+vinyl@^2.0.0:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-2.2.1.tgz#23cfb8bbab5ece3803aa2c0a1eb28af7cbba1974"
+ integrity sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw==
+ dependencies:
+ clone "^2.1.1"
+ clone-buffer "^1.0.0"
+ clone-stats "^1.0.0"
+ cloneable-readable "^1.0.0"
+ remove-trailing-separator "^1.0.1"
+ replace-ext "^1.0.0"
+
+vinyl@~3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-3.0.0.tgz#11e14732bf56e2faa98ffde5157fe6c13259ff30"
+ integrity sha512-rC2VRfAVVCGEgjnxHUnpIVh3AGuk62rP3tqVrn+yab0YH7UULisC085+NYH+mnqf3Wx4SpSi1RQMwudL89N03g==
+ dependencies:
+ clone "^2.1.2"
+ clone-stats "^1.0.0"
+ remove-trailing-separator "^1.1.0"
+ replace-ext "^2.0.0"
+ teex "^1.0.1"
+
vite-plugin-html-template@^1.1.0:
version "1.1.5"
resolved "https://registry.yarnpkg.com/vite-plugin-html-template/-/vite-plugin-html-template-1.1.5.tgz#9d64c34b702fe3675ab3c3e55bfe65c044720401"
@@ -12174,6 +12842,29 @@ vm-browserify@^1.0.1:
resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0"
integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==
+void-elements@3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09"
+ integrity sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==
+
+vue-template-compiler@^2.6.11:
+ version "2.7.10"
+ resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.7.10.tgz#9e20f35b2fdccacacf732dd7dedb49bf65f4556b"
+ integrity sha512-QO+8R9YRq1Gudm8ZMdo/lImZLJVUIAM8c07Vp84ojdDAf8HmPJc7XB556PcXV218k2AkKznsRz6xB5uOjAC4EQ==
+ dependencies:
+ de-indent "^1.0.2"
+ he "^1.2.0"
+
+walk-sync@^2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/walk-sync/-/walk-sync-2.2.0.tgz#80786b0657fcc8c0e1c0b1a042a09eae2966387a"
+ integrity sha512-IC8sL7aB4/ZgFcGI2T1LczZeFWZ06b3zoHH7jBPyHxOtIIz1jppWHjjEXkOFvFojBVAK9pV7g47xOZ4LW3QLfg==
+ dependencies:
+ "@types/minimatch" "^3.0.3"
+ ensure-posix-path "^1.1.0"
+ matcher-collection "^2.0.0"
+ minimatch "^3.0.4"
+
watchpack-chokidar2@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz#38500072ee6ece66f3769936950ea1771be1c957"
@@ -12420,7 +13111,7 @@ x-default-browser@^0.4.0:
optionalDependencies:
default-browser-id "^1.0.4"
-xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.1:
+xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.0, xtend@~4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==