mirror of
https://github.com/vector-im/element-web.git
synced 2026-05-05 20:26:19 +02:00
WIP refactor of i18n
To put traslation logic back into element web but split the keys up so everything is not type dependenct on everything else for the translation keys.
This commit is contained in:
parent
0eff1caab2
commit
e5f227531a
@ -29,7 +29,7 @@
|
||||
"UserFriendlyError"
|
||||
],
|
||||
"scripts": {
|
||||
"i18n": "matrix-gen-i18n src res packages/shared-components/src && yarn i18n:sort && yarn i18n:lint",
|
||||
"i18n": "matrix-gen-i18n src res && yarn i18n:sort && yarn i18n:lint",
|
||||
"i18n:sort": "jq --sort-keys '.' src/i18n/strings/en_EN.json > src/i18n/strings/en_EN.json.tmp && mv src/i18n/strings/en_EN.json.tmp src/i18n/strings/en_EN.json",
|
||||
"i18n:lint": "matrix-i18n-lint && prettier --log-level=silent --write src/i18n/strings/ --ignore-path /dev/null",
|
||||
"i18n:diff": "cp src/i18n/strings/en_EN.json src/i18n/strings/en_EN_orig.json && yarn i18n && matrix-compare-i18n-files src/i18n/strings/en_EN_orig.json src/i18n/strings/en_EN.json",
|
||||
|
||||
@ -34,8 +34,11 @@
|
||||
"package.json"
|
||||
],
|
||||
"scripts": {
|
||||
"i18n": "matrix-gen-i18n src && yarn i18n:sort && yarn i18n:lint",
|
||||
"i18n:sort": "jq --sort-keys '.' src/i18n/strings/en_EN.json > src/i18n/strings/en_EN.json.tmp && mv src/i18n/strings/en_EN.json.tmp src/i18n/strings/en_EN.json",
|
||||
"i18n:lint": "matrix-i18n-lint && prettier --log-level=silent --write src/i18n/strings/ --ignore-path /dev/null",
|
||||
"test": "jest",
|
||||
"prepare": "patch-package && yarn --cwd ../.. build:res && node scripts/gatherTranslationKeys.ts && vite build",
|
||||
"prepare": "patch-package && vite build",
|
||||
"storybook": "storybook dev -p 6007",
|
||||
"build-storybook": "storybook build",
|
||||
"lint": "yarn lint:types && yarn lint:js",
|
||||
@ -46,6 +49,7 @@
|
||||
"test:storybook:update": "playwright-screenshots --entrypoint /work/node_modules/.bin/test-storybook --with-node-modules --url http://host.docker.internal:6007/ --updateSnapshot"
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-hq/element-web-module-api": "^1.5.0",
|
||||
"classnames": "^2.5.1",
|
||||
"counterpart": "^0.18.6",
|
||||
"lodash": "^4.17.21",
|
||||
|
||||
@ -1,67 +0,0 @@
|
||||
/*
|
||||
Copyright 2025 Element Creations Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
// Gathers all the translation keys from element-web's en_EN.json into a TypeScript type definition file
|
||||
// that exports a type `TranslationKey` which is a union of all supported translation keys.
|
||||
// This prevents having to import the json file and make typescript do the work as this results in vite-dts
|
||||
// generating an import to the json file in the .d.ts which doesn't work at runtime: this way, the type
|
||||
// gets put into the bundle.
|
||||
// XXX: It should *not* be in the 'src' directory, being a generated file, but if it isn't then the type
|
||||
// bundler won't bundle the types and will leave the file as a relative import, which will break.
|
||||
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
import { dirname } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const i18nStringsPath = path.resolve(__dirname, "../../../src/i18n/strings/en_EN.json");
|
||||
const outPath = path.resolve(__dirname, "../src/i18nKeys.d.ts");
|
||||
|
||||
function gatherKeys(obj: any, prefix: string[] = []): string[] {
|
||||
if (typeof obj !== "object" || obj === null) return [];
|
||||
let keys: string[] = [];
|
||||
for (const key of Object.keys(obj)) {
|
||||
const value = obj[key];
|
||||
|
||||
// add the path (for both leaves and intermediates as then we include plurals)
|
||||
keys.push([...prefix, key].join("|"));
|
||||
if (typeof value === "object" && value !== null) {
|
||||
// If the value is an object, recurse
|
||||
keys = keys.concat(gatherKeys(value, [...prefix, key]));
|
||||
}
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
function main() {
|
||||
const json = JSON.parse(fs.readFileSync(i18nStringsPath, "utf8"));
|
||||
const keys = gatherKeys(json);
|
||||
const typeDef =
|
||||
"/*\n" +
|
||||
" * Copyright 2025 Element Creations Ltd.\n" +
|
||||
" *\n" +
|
||||
" * SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial\n" +
|
||||
" * Please see LICENSE files in the repository root for full details.\n" +
|
||||
" */\n" +
|
||||
"\n" +
|
||||
"// This file is auto-generated by gatherTranslationKeys.ts\n" +
|
||||
"// Do not edit manually.\n\n" +
|
||||
"export type TranslationKey =\n" +
|
||||
keys.map((k) => ` | \"${k}\"`).join("\n") +
|
||||
";\n";
|
||||
fs.mkdirSync(path.dirname(outPath), { recursive: true });
|
||||
fs.writeFileSync(outPath, typeDef, "utf8");
|
||||
console.log(`Wrote ${keys.length} keys to ${outPath}`);
|
||||
}
|
||||
|
||||
if (import.meta.url.startsWith("file:")) {
|
||||
const modulePath = fileURLToPath(import.meta.url);
|
||||
if (process.argv[1] === modulePath) {
|
||||
main();
|
||||
}
|
||||
}
|
||||
@ -14,7 +14,7 @@ import { Flex } from "../../utils/Flex";
|
||||
import styles from "./AudioPlayerView.module.css";
|
||||
import { PlayPauseButton } from "../PlayPauseButton";
|
||||
import { type PlaybackState } from "../playback";
|
||||
import { _t } from "../../utils/i18n";
|
||||
import { _t } from "__i18nAPI";
|
||||
import { formatBytes } from "../../utils/FormattingUtils";
|
||||
import { Clock } from "../Clock";
|
||||
import { SeekBar } from "../SeekBar";
|
||||
|
||||
@ -11,7 +11,7 @@ import Play from "@vector-im/compound-design-tokens/assets/web/icons/play-solid"
|
||||
import Pause from "@vector-im/compound-design-tokens/assets/web/icons/pause-solid";
|
||||
|
||||
import styles from "./PlayPauseButton.module.css";
|
||||
import { _t } from "../../utils/i18n";
|
||||
import { _t } from "__i18nAPI";
|
||||
|
||||
export interface PlayPauseButtonProps extends HTMLAttributes<HTMLButtonElement> {
|
||||
/**
|
||||
|
||||
@ -10,7 +10,7 @@ import { throttle } from "lodash";
|
||||
import classNames from "classnames";
|
||||
|
||||
import style from "./SeekBar.module.css";
|
||||
import { _t } from "../../utils/i18n";
|
||||
import { _t } from "__i18nAPI";
|
||||
|
||||
export interface SeekBarProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
||||
/**
|
||||
|
||||
17
packages/shared-components/src/i18n/strings/en_EN.json
Normal file
17
packages/shared-components/src/i18n/strings/en_EN.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"a11y": {
|
||||
"seek_bar_label": "a11y|seek_bar_label"
|
||||
},
|
||||
"action": {
|
||||
"delete": "action|delete",
|
||||
"pause": "action|pause",
|
||||
"play": "action|play"
|
||||
},
|
||||
"timeline": {
|
||||
"m.audio": {
|
||||
"audio_player": "timeline|m.audio|audio_player",
|
||||
"error_downloading_audio": "timeline|m.audio|error_downloading_audio",
|
||||
"unnamed_audio": "timeline|m.audio|unnamed_audio"
|
||||
}
|
||||
}
|
||||
}
|
||||
14
packages/shared-components/src/i18nStub.ts
Normal file
14
packages/shared-components/src/i18nStub.ts
Normal file
@ -0,0 +1,14 @@
|
||||
/*
|
||||
Copyright 2025 Element Creations Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import type Translations from "./i18n/strings/en_EN.json";
|
||||
import { type TranslationKey as TranslationKeyType } from "matrix-web-i18n";
|
||||
import { translateFn, humanizeTimeFn } from "matrix-web-i18n";
|
||||
type TranslationKey = TranslationKeyType<typeof Translations>;
|
||||
|
||||
export const _t: translateFn<TranslationKey> = () => "";
|
||||
export const humanizeTime: humanizeTimeFn = () => "";
|
||||
@ -21,8 +21,6 @@ export * from "./utils/Box";
|
||||
export * from "./utils/Flex";
|
||||
|
||||
// Utils
|
||||
export * from "./utils/i18n";
|
||||
export * from "./utils/humanize";
|
||||
export * from "./utils/DateUtils";
|
||||
export * from "./utils/numbers";
|
||||
export * from "./utils/FormattingUtils";
|
||||
@ -31,7 +29,3 @@ export * from "./utils/FormattingUtils";
|
||||
export * from "./viewmodel";
|
||||
export * from "./useMockedViewModel";
|
||||
export * from "./useViewModel";
|
||||
|
||||
// i18n (we must export this directly in order to not confuse the type bundler, it seems,
|
||||
// otherwise it will leave it as a relative import rather than bundling it)
|
||||
export type * from "./i18nKeys.d.ts";
|
||||
|
||||
@ -12,7 +12,7 @@ import CloseIcon from "@vector-im/compound-design-tokens/assets/web/icons/close"
|
||||
|
||||
import { Flex } from "../../utils/Flex";
|
||||
import styles from "./Pill.module.css";
|
||||
import { _t } from "../../utils/i18n";
|
||||
import { _t } from "__i18nAPI";
|
||||
|
||||
export interface PillProps extends Omit<HTMLAttributes<HTMLDivElement>, "onClick"> {
|
||||
/**
|
||||
|
||||
@ -9,7 +9,7 @@ import React, { type HTMLAttributes, type JSX, memo } from "react";
|
||||
import CheckIcon from "@vector-im/compound-design-tokens/assets/web/icons/check";
|
||||
|
||||
import styles from "./RichItem.module.css";
|
||||
import { humanizeTime } from "../../utils/humanize";
|
||||
import { humanizeTime } from "__i18nAPI";
|
||||
import { Flex } from "../../utils/Flex";
|
||||
|
||||
export interface RichItemProps extends HTMLAttributes<HTMLLIElement> {
|
||||
|
||||
@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details.
|
||||
|
||||
import fetchMock from "fetch-mock-jest";
|
||||
|
||||
import { setLanguage } from "../../src/utils/i18n";
|
||||
import { setLanguage } from "__i18nAPI";
|
||||
import en from "../../../../src/i18n/strings/en_EN.json";
|
||||
|
||||
export function setupLanguageMock(): void {
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
"lib": ["es2022", "es2024.promise", "dom", "dom.iterable"],
|
||||
"strict": true,
|
||||
"paths": {
|
||||
"__i18nAPI": ["./src/i18nStub.ts"],
|
||||
"jest-matrix-react": ["./src/test/utils/jest-matrix-react"],
|
||||
"rollup/parseAst": ["./node_modules/rollup/dist/parseAst"]
|
||||
}
|
||||
|
||||
@ -26,13 +26,14 @@ export default defineConfig({
|
||||
rollupOptions: {
|
||||
// make sure to externalize deps that shouldn't be bundled
|
||||
// into your library
|
||||
external: ["react", "react-dom", "@vector-im/compound-design-tokens", "@vector-im/compound-web"],
|
||||
external: ["react", "react-dom", "@vector-im/compound-design-tokens", "@vector-im/compound-web", "__i18nAPI"],
|
||||
output: {
|
||||
// Provide global variables to use in the UMD build
|
||||
// for externalized deps
|
||||
globals: {
|
||||
"react": "react",
|
||||
"react-dom": "ReactDom",
|
||||
"__i18nAPI": "mxI18nAPI",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@ -352,6 +352,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
||||
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
||||
|
||||
"@element-hq/element-web-module-api@^1.5.0":
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@element-hq/element-web-module-api/-/element-web-module-api-1.5.0.tgz#077a528917f4eb558059a2a5286b9bb6a2fb1690"
|
||||
integrity sha512-WI/iMADRouXp9WhQy5jov6Z4eKKlHEPh20DKoCsKZ9dWaYcW/MiBhzi09PZxay+o0RLZXA6aDPxpxaIX3lZXag==
|
||||
|
||||
"@element-hq/element-web-playwright-common@^2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@element-hq/element-web-playwright-common/-/element-web-playwright-common-2.0.0.tgz#30cf741a33c69540b4bc434f5349d0fe900bc611"
|
||||
|
||||
3
src/@types/global.d.ts
vendored
3
src/@types/global.d.ts
vendored
@ -10,7 +10,7 @@ Please see LICENSE files in the repository root for full details.
|
||||
import "matrix-js-sdk/src/@types/global"; // load matrix-js-sdk's type extensions first
|
||||
import "@types/modernizr";
|
||||
|
||||
import type { ModuleLoader } from "@element-hq/element-web-module-api";
|
||||
import type { I18nApi, ModuleLoader } from "@element-hq/element-web-module-api";
|
||||
import type { logger } from "matrix-js-sdk/src/logger";
|
||||
import type ContentMessages from "../ContentMessages";
|
||||
import { type IMatrixClientPeg } from "../MatrixClientPeg";
|
||||
@ -125,6 +125,7 @@ declare global {
|
||||
mxOnRecaptchaLoaded?: () => void;
|
||||
mxModuleLoader: ModuleLoader;
|
||||
mxModuleApi: ModuleApiType;
|
||||
mxI18nAPI: I18nApi;
|
||||
|
||||
// electron-only
|
||||
electron?: Electron;
|
||||
|
||||
@ -8,12 +8,11 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
// Import i18n.tsx instead of languageHandler to avoid circular deps
|
||||
import { _td, type TranslationKey } from "@element-hq/web-shared-components";
|
||||
|
||||
import { IS_MAC, IS_ELECTRON, Key } from "../Keyboard";
|
||||
import { type IBaseSetting } from "../settings/Settings";
|
||||
import { type KeyCombo } from "../KeyBindingsManager";
|
||||
import { type TranslationKey } from "../i18n/i18n";
|
||||
import { _td } from "../i18n/i18n";
|
||||
|
||||
export enum KeyBindingAction {
|
||||
/** Send a message */
|
||||
|
||||
@ -8,7 +8,6 @@ Please see LICENSE files in the repository root for full details.
|
||||
|
||||
import React, { type HTMLProps, useContext } from "react";
|
||||
import { type Beacon, BeaconEvent, LocationAssetType } from "matrix-js-sdk/src/matrix";
|
||||
import { humanizeTime } from "@element-hq/web-shared-components";
|
||||
|
||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||
import { useEventEmitterState } from "../../../hooks/useEventEmitter";
|
||||
@ -19,6 +18,7 @@ import BeaconStatus from "./BeaconStatus";
|
||||
import { BeaconDisplayStatus } from "./displayStatus";
|
||||
import StyledLiveBeaconIcon from "./StyledLiveBeaconIcon";
|
||||
import ShareLatestLocation from "./ShareLatestLocation";
|
||||
import { humanizeTime } from "../../../i18n/humanize";
|
||||
|
||||
interface Props {
|
||||
beacon: Beacon;
|
||||
|
||||
@ -6,13 +6,13 @@ Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React, { type JSX, useCallback, useId, useState } from "react";
|
||||
import { _t } from "@element-hq/web-shared-components";
|
||||
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import { type SettingLevel } from "../../../settings/SettingLevel";
|
||||
import { SETTINGS, type StringSettingKey } from "../../../settings/Settings";
|
||||
import { useSettingValueAt } from "../../../hooks/useSettings.ts";
|
||||
import Dropdown, { type DropdownProps } from "./Dropdown.tsx";
|
||||
import { _t } from "../../../languageHandler.tsx";
|
||||
|
||||
interface Props {
|
||||
settingKey: StringSettingKey;
|
||||
|
||||
@ -30,7 +30,7 @@ import {
|
||||
VectorState,
|
||||
type VectorPushRuleDefinition,
|
||||
} from "../../../notifications";
|
||||
import { _t, type TranslatedString } from "../../../languageHandler";
|
||||
import { _t } from "../../../languageHandler";
|
||||
import LabelledToggleSwitch from "../elements/LabelledToggleSwitch";
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import StyledRadioButton from "../elements/StyledRadioButton";
|
||||
@ -52,6 +52,7 @@ import { SettingsSubsectionHeading } from "./shared/SettingsSubsectionHeading";
|
||||
import { SettingsSubsection } from "./shared/SettingsSubsection";
|
||||
import { doesRoomHaveUnreadMessages } from "../../../Unread";
|
||||
import SettingsFlag from "../elements/SettingsFlag";
|
||||
import { TranslatedString } from "../../../i18n/i18n";
|
||||
|
||||
// TODO: this "view" component still has far too much application logic in it,
|
||||
// which should be factored out to other files.
|
||||
|
||||
@ -10,8 +10,9 @@ import React from "react";
|
||||
import { type Device } from "matrix-js-sdk/src/matrix";
|
||||
import { type GeneratedSas, type EmojiMapping } from "matrix-js-sdk/src/crypto-api";
|
||||
import SasEmoji from "@matrix-org/spec/sas-emoji.json";
|
||||
import { getNormalizedLanguageKeys } from "matrix-web-i18n";
|
||||
|
||||
import { _t, getNormalizedLanguageKeys, getUserLanguage } from "../../../languageHandler";
|
||||
import { _t, getUserLanguage } from "../../../languageHandler";
|
||||
import { PendingActionSpinner } from "../right_panel/EncryptionInfo";
|
||||
import AccessibleButton from "../elements/AccessibleButton";
|
||||
|
||||
|
||||
@ -24,13 +24,14 @@
|
||||
import React from "react";
|
||||
import { KEY_SEPARATOR } from "matrix-web-i18n";
|
||||
import counterpart from "counterpart";
|
||||
import { type TranslationKey as TranslationKeyType } from "matrix-web-i18n";
|
||||
|
||||
import type { TranslationKey } from "../index";
|
||||
import type Translations from "../i18n/strings/en_EN.json";
|
||||
|
||||
// @ts-ignore - $webapp is a webpack resolve alias pointing to the output directory, see webpack config
|
||||
import webpackLangJsonUrl from "$webapp/i18n/languages.json";
|
||||
|
||||
export { KEY_SEPARATOR, normalizeLanguageKey, getNormalizedLanguageKeys } from "matrix-web-i18n";
|
||||
export type TranslationKey = TranslationKeyType<typeof Translations>;
|
||||
|
||||
const i18nFolder = "i18n/";
|
||||
|
||||
@ -3463,11 +3463,9 @@
|
||||
"unable_to_find": "Tried to load a specific point in this room's timeline, but was unable to find it."
|
||||
},
|
||||
"m.audio": {
|
||||
"audio_player": "Audio player",
|
||||
"error_downloading_audio": "Error downloading audio",
|
||||
"error_processing_audio": "Error processing audio message",
|
||||
"error_processing_voice_message": "Error processing voice message",
|
||||
"unnamed_audio": "Unnamed audio"
|
||||
"error_processing_voice_message": "Error processing voice message"
|
||||
},
|
||||
"m.beacon_info": {
|
||||
"view_live_location": "View live location"
|
||||
|
||||
@ -8,21 +8,20 @@
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { type Optional } from "matrix-events-sdk";
|
||||
import { MapWithDefault } from "matrix-js-sdk/src/utils";
|
||||
import { KEY_SEPARATOR, normalizeLanguageKey } from "matrix-web-i18n";
|
||||
import { type TranslationStringsObject } from "@matrix-org/react-sdk-module-api";
|
||||
import _ from "lodash";
|
||||
|
||||
import {
|
||||
_t,
|
||||
normalizeLanguageKey,
|
||||
type TranslationKey,
|
||||
type IVariables,
|
||||
KEY_SEPARATOR,
|
||||
getLangsJson,
|
||||
registerTranslations,
|
||||
setLocale,
|
||||
getLocale,
|
||||
setMissingEntryGenerator as setMissingEntryGeneratorSharedComponents,
|
||||
} from "@element-hq/web-shared-components";
|
||||
|
||||
type TranslationKey,
|
||||
} from "./i18n/i18n";
|
||||
import SettingsStore from "./settings/SettingsStore";
|
||||
import PlatformPeg from "./PlatformPeg";
|
||||
import { SettingLevel } from "./settings/SettingLevel";
|
||||
@ -30,20 +29,7 @@ import { retry } from "./utils/promise";
|
||||
import SdkConfig from "./SdkConfig";
|
||||
import { ModuleRunner } from "./modules/ModuleRunner";
|
||||
|
||||
export {
|
||||
_t,
|
||||
type IVariables,
|
||||
type Tags,
|
||||
type TranslationKey,
|
||||
type TranslatedString,
|
||||
_td,
|
||||
_tDom,
|
||||
lookupString,
|
||||
sanitizeForTranslation,
|
||||
normalizeLanguageKey,
|
||||
getNormalizedLanguageKeys,
|
||||
substitute,
|
||||
} from "@element-hq/web-shared-components";
|
||||
export { _t, _td, _tDom, type TranslationKey } from "./i18n/i18n";
|
||||
|
||||
const i18nFolder = "i18n/";
|
||||
|
||||
|
||||
@ -6,9 +6,11 @@ Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { type I18nApi as II18nApi, type Variables, type Translations } from "@element-hq/element-web-module-api";
|
||||
import { registerTranslations } from "@element-hq/web-shared-components";
|
||||
|
||||
import { _t, getCurrentLanguage, type TranslationKey } from "../languageHandler.tsx";
|
||||
import { registerTranslations, _t } from "../i18n/i18n.ts";
|
||||
import { getCurrentLanguage } from "../languageHandler.tsx";
|
||||
import { humanizeTime } from "../i18n/humanize.ts";
|
||||
import { type TranslationKey } from "../i18n/i18n.tsx";
|
||||
|
||||
export class I18nApi implements II18nApi {
|
||||
/**
|
||||
@ -44,4 +46,10 @@ export class I18nApi implements II18nApi {
|
||||
public translate(key: TranslationKey, variables?: Variables): string {
|
||||
return _t(key, variables);
|
||||
}
|
||||
|
||||
public humanizeTime(timeMillis: number): string {
|
||||
return humanizeTime(timeMillis);
|
||||
}
|
||||
|
||||
public async setLanguage(language: string): Promise<void> {}
|
||||
}
|
||||
|
||||
@ -9,8 +9,6 @@ Please see LICENSE files in the repository root for full details.
|
||||
|
||||
import React, { type ReactNode } from "react";
|
||||
import { STABLE_MSC4133_EXTENDED_PROFILES, UNSTABLE_MSC4133_EXTENDED_PROFILES } from "matrix-js-sdk/src/matrix";
|
||||
// Import these directly from shared-components to avoid circular deps
|
||||
import { _t, _td, type TranslationKey } from "@element-hq/web-shared-components";
|
||||
|
||||
import { type MediaPreviewConfig } from "../@types/media_preview.ts";
|
||||
import DeviceIsolationModeController from "./controllers/DeviceIsolationModeController.ts";
|
||||
@ -50,6 +48,8 @@ import { SortingAlgorithm } from "../stores/room-list-v3/skip-list/sorters/index
|
||||
import MediaPreviewConfigController from "./controllers/MediaPreviewConfigController.ts";
|
||||
import InviteRulesConfigController from "./controllers/InviteRulesConfigController.ts";
|
||||
import { type ComputedInviteConfig } from "../@types/invite-rules.ts";
|
||||
import { type TranslationKey } from "../i18n/i18n.ts";
|
||||
import { _t, _td } from "../i18n/i18n.ts";
|
||||
|
||||
export const defaultWatchManager = new WatchManager();
|
||||
|
||||
|
||||
@ -10,10 +10,11 @@ import { type MatrixEvent, MsgType, RelationType } from "matrix-js-sdk/src/matri
|
||||
|
||||
import { type IPreview } from "./IPreview";
|
||||
import { type TagID } from "../models";
|
||||
import { _t, sanitizeForTranslation } from "../../../languageHandler";
|
||||
import { _t } from "../../../languageHandler";
|
||||
import { getSenderName, isSelf, shouldPrefixMessagesIn } from "./utils";
|
||||
import { getHtmlText } from "../../../HtmlUtils";
|
||||
import { stripHTMLReply, stripPlainReply } from "../../../utils/Reply";
|
||||
import { sanitizeForTranslation } from "../../../i18n/i18n";
|
||||
|
||||
export class MessageEventPreview implements IPreview {
|
||||
public getTextFor(event: MatrixEvent, tagId?: TagID, isThread?: boolean): string | null {
|
||||
|
||||
@ -12,9 +12,10 @@ import { PollStartEvent } from "matrix-js-sdk/src/extensible_events_v1/PollStart
|
||||
|
||||
import { type IPreview } from "./IPreview";
|
||||
import { type TagID } from "../models";
|
||||
import { _t, sanitizeForTranslation } from "../../../languageHandler";
|
||||
import { _t } from "../../../languageHandler";
|
||||
import { getSenderName, isSelf, shouldPrefixMessagesIn } from "./utils";
|
||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||
import { sanitizeForTranslation } from "../../../i18n/i18n";
|
||||
|
||||
export class PollStartEventPreview implements IPreview {
|
||||
public static contextType = MatrixClientContext;
|
||||
|
||||
@ -10,12 +10,13 @@ import React, { type ReactNode } from "react";
|
||||
import { MatrixError, ConnectionError } from "matrix-js-sdk/src/matrix";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
|
||||
import { _t, _td, lookupString, type Tags, type TranslatedString, type TranslationKey } from "../languageHandler";
|
||||
import { _t, _td, type TranslationKey } from "../languageHandler";
|
||||
import SdkConfig from "../SdkConfig";
|
||||
import { type ValidatedServerConfig } from "./ValidatedServerConfig";
|
||||
import ExternalLink from "../components/views/elements/ExternalLink";
|
||||
import Modal from "../Modal.tsx";
|
||||
import ErrorDialog from "../components/views/dialogs/ErrorDialog.tsx";
|
||||
import { lookupString, type Tags, type TranslatedString } from "../i18n/i18n.tsx";
|
||||
|
||||
export const resourceLimitStrings = {
|
||||
"monthly_active_user": _td("error|mau"),
|
||||
|
||||
@ -16,6 +16,7 @@ import { shouldPolyfill as shouldPolyFillIntlSegmenter } from "@formatjs/intl-se
|
||||
// These are things that can run before the skin loads - be careful not to reference the react-sdk though.
|
||||
import { parseQsFromFragment } from "./url_utils";
|
||||
import "./modernizr";
|
||||
import { _t } from "../languageHandler";
|
||||
|
||||
// Import shared components CSS
|
||||
import "@element-hq/web-shared-components/dist/element-web-shared-components.css";
|
||||
@ -122,7 +123,6 @@ async function start(): Promise<void> {
|
||||
loadPlugins,
|
||||
showError,
|
||||
showIncompatibleBrowser,
|
||||
_t,
|
||||
extractErrorMessageFromError,
|
||||
} = await import(
|
||||
/* webpackChunkName: "init" */
|
||||
|
||||
@ -12,6 +12,7 @@ import { createRoot } from "react-dom/client";
|
||||
import React, { StrictMode } from "react";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { ModuleLoader } from "@element-hq/element-web-module-api";
|
||||
import { getNormalizedLanguageKeys } from "matrix-web-i18n";
|
||||
|
||||
import type { QueryDict } from "matrix-js-sdk/src/utils";
|
||||
import * as languageHandler from "../languageHandler";
|
||||
@ -69,7 +70,7 @@ export async function loadLanguage(): Promise<void> {
|
||||
|
||||
if (!prefLang) {
|
||||
languageHandler.getLanguagesFromBrowser().forEach((l) => {
|
||||
langs.push(...languageHandler.getNormalizedLanguageKeys(l));
|
||||
langs.push(...getNormalizedLanguageKeys(l));
|
||||
});
|
||||
} else {
|
||||
langs = [prefLang];
|
||||
@ -142,6 +143,7 @@ export async function loadPlugins(): Promise<void> {
|
||||
// every single module to ship its own copy of React. This also makes it easier to access via the console
|
||||
// and incidentally means we can forget our React imports in JSX files without penalty.
|
||||
window.React = React;
|
||||
window.mxI18nAPI = ModuleApi.instance.i18n;
|
||||
|
||||
const modules = SdkConfig.get("modules");
|
||||
if (!modules?.length) return;
|
||||
@ -155,6 +157,4 @@ export async function loadPlugins(): Promise<void> {
|
||||
await moduleLoader.start();
|
||||
}
|
||||
|
||||
export { _t } from "../languageHandler";
|
||||
|
||||
export { extractErrorMessageFromError } from "../components/views/dialogs/ErrorDialog";
|
||||
|
||||
@ -21,10 +21,11 @@ import {
|
||||
import { EventType, MsgType } from "matrix-js-sdk/src/matrix";
|
||||
import React from "react";
|
||||
|
||||
import { _t, _td, type TranslatedString, type TranslationKey } from "../languageHandler";
|
||||
import { _t, _td, type TranslationKey } from "../languageHandler";
|
||||
import { ElementWidgetCapabilities } from "../stores/widgets/ElementWidgetCapabilities";
|
||||
import { MatrixClientPeg } from "../MatrixClientPeg";
|
||||
import TextWithTooltip from "../components/views/elements/TextWithTooltip";
|
||||
import { type TranslatedString } from "../i18n/i18n";
|
||||
|
||||
type GENERIC_WIDGET_KIND = "generic"; // eslint-disable-line @typescript-eslint/naming-convention
|
||||
const GENERIC_WIDGET_KIND: GENERIC_WIDGET_KIND = "generic";
|
||||
|
||||
@ -20,16 +20,13 @@ import {
|
||||
registerCustomTranslations,
|
||||
setLanguage,
|
||||
setMissingEntryGenerator,
|
||||
substitute,
|
||||
type TranslatedString,
|
||||
UserFriendlyError,
|
||||
type TranslationKey,
|
||||
type IVariables,
|
||||
type Tags,
|
||||
getLanguagesFromBrowser,
|
||||
} from "../../src/languageHandler";
|
||||
import { stubClient } from "../test-utils";
|
||||
import { setupLanguageMock } from "../setup/setupLanguage";
|
||||
import { substitute, type IVariables, type Tags, type TranslatedString } from "../../src/i18n/i18n";
|
||||
|
||||
async function setupTranslationOverridesForTests(overrides: TranslationStringsObject) {
|
||||
const lookupUrl = "/translations.json";
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user