Merge branch 'develop' into t3chguy/compound-icons-20260106

This commit is contained in:
Michael Telatynski 2026-01-07 13:05:29 +00:00 committed by GitHub
commit 76da495987
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
167 changed files with 2283 additions and 2537 deletions

View File

@ -66,7 +66,7 @@ jobs:
run: VERSION=$(scripts/get-version-from-git.sh) yarn build
- name: Upload Artifact
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: webapp-${{ matrix.image }}
path: webapp

View File

@ -62,7 +62,7 @@ jobs:
dpkg-gencontrol -v"$VERSION" -ldebian/tmp/DEBIAN/changelog
dpkg-deb -Zxz --root-owner-group --build debian/tmp element-web.deb
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
- uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: element-web.deb
path: element-web.deb

View File

@ -53,7 +53,7 @@ jobs:
- run: mv dist/element-*.tar.gz dist/develop.tar.gz
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
- uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: webapp
path: dist/develop.tar.gz

View File

@ -25,7 +25,7 @@ jobs:
actions: read
steps:
- name: Download HTML report
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
run-id: ${{ github.event.workflow_run.id }}

View File

@ -74,7 +74,7 @@ jobs:
run: VERSION=$(scripts/get-version-from-git.sh) yarn build
- name: Upload Artifact
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: webapp
path: webapp
@ -128,7 +128,7 @@ jobs:
repository: element-hq/element-web
- name: 📥 Download artifact
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
with:
name: webapp
path: webapp
@ -172,7 +172,7 @@ jobs:
- name: Upload blob report to GitHub Actions Artifacts
if: always()
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: all-blob-reports-${{ matrix.project }}-${{ matrix.runner }}
path: blob-report
@ -212,7 +212,7 @@ jobs:
- name: Download blob reports from GitHub Actions Artifacts
if: inputs.skip != true
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
with:
pattern: all-blob-reports-*
path: all-blob-reports
@ -228,7 +228,7 @@ jobs:
# Upload the HTML report even if one of our reporters fails, this can happen when stale screenshots are detected
- name: Upload HTML report
if: always() && inputs.skip != true
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: html-report
path: playwright-report

View File

@ -28,7 +28,7 @@ jobs:
Exercise caution. Use test accounts.
- name: 📥 Download artifact
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
run-id: ${{ github.event.workflow_run.id }}

View File

@ -32,7 +32,7 @@ jobs:
- name: Create Pull Request
id: cpr
uses: peter-evans/create-pull-request@22a9089034f40e5a961c8808d113e2c98fb63676 # v7
uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725 # v8
with:
token: ${{ secrets.ELEMENT_BOT_TOKEN }}
branch: actions/playwright-image-updates

View File

@ -27,7 +27,7 @@ jobs:
run: "sudo apt-get install -y tree"
- name: Download Diffs
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
run-id: ${{ github.event.workflow_run.id }}

View File

@ -60,7 +60,7 @@ jobs:
- name: Upload received images & diffs
if: always()
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: received-images
path: packages/shared-components/playwright/shared-component-received

View File

@ -41,8 +41,8 @@ jobs:
- name: Typecheck Shared Components
run: "yarn --cwd packages/shared-components run lint:types"
i18n_lint:
name: "i18n Check"
i18n_lint_ew:
name: "i18n Check (Element Web)"
uses: matrix-org/matrix-web-i18n/.github/workflows/i18n_check.yml@main
permissions:
pull-requests: read
@ -59,6 +59,15 @@ jobs:
devtools|settings|elementCallUrl
labs|sliding_sync_description
i18n_lint_shared_components:
name: "i18n Check (Shared Components)"
uses: matrix-org/matrix-web-i18n/.github/workflows/i18n_check.yml@main
permissions:
pull-requests: read
with:
path: "packages/shared-components"
hardcoded-words: "Element"
rethemendex_lint:
name: "Rethemendex Check"
runs-on: ubuntu-24.04

View File

@ -84,7 +84,7 @@ jobs:
- name: Upload Artifact
if: env.ENABLE_COVERAGE == 'true'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: coverage-${{ matrix.runner }}
path: |
@ -159,7 +159,7 @@ jobs:
- name: Upload Artifact
if: env.ENABLE_COVERAGE == 'true'
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: coverage-sharedcomponents
path: |

View File

@ -9,7 +9,7 @@ jobs:
name: Move PRs asking for design review to the design board
runs-on: ubuntu-24.04
steps:
- uses: octokit/graphql-action@abaeca7ba4f0325d63b8de7ef943c2418d161b93 # v3.0.0
- uses: octokit/graphql-action@ddde8ebb2493e79f390e6449c725c21663a67505 # v3.0.2
id: find_team_members
with:
headers: '{"GraphQL-Features": "projects_next_graphql"}'
@ -52,7 +52,7 @@ jobs:
fi
env:
TEAM: "design"
- uses: octokit/graphql-action@abaeca7ba4f0325d63b8de7ef943c2418d161b93 # v3.0.0
- uses: octokit/graphql-action@ddde8ebb2493e79f390e6449c725c21663a67505 # v3.0.2
id: add_to_project
if: steps.any_matching_reviewers.outputs.match == 'true'
with:
@ -76,7 +76,7 @@ jobs:
name: Move PRs asking for design review to the design board
runs-on: ubuntu-24.04
steps:
- uses: octokit/graphql-action@abaeca7ba4f0325d63b8de7ef943c2418d161b93 # v3.0.0
- uses: octokit/graphql-action@ddde8ebb2493e79f390e6449c725c21663a67505 # v3.0.2
id: find_team_members
with:
headers: '{"GraphQL-Features": "projects_next_graphql"}'
@ -119,7 +119,7 @@ jobs:
fi
env:
TEAM: "product"
- uses: octokit/graphql-action@abaeca7ba4f0325d63b8de7ef943c2418d161b93 # v3.0.0
- uses: octokit/graphql-action@ddde8ebb2493e79f390e6449c725c21663a67505 # v3.0.2
id: add_to_project
if: steps.any_matching_reviewers.outputs.match == 'true'
with:

View File

@ -23,7 +23,7 @@ jobs:
run: "yarn update:jitsi"
- name: Create Pull Request
uses: peter-evans/create-pull-request@22a9089034f40e5a961c8808d113e2c98fb63676 # v7
uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725 # v8
with:
token: ${{ secrets.ELEMENT_BOT_TOKEN }}
branch: actions/jitsi-update

View File

@ -18,6 +18,18 @@
"file": "element-web.json",
"excludes": ["src/i18n/strings/en_EN.json"],
"lang": "${autodetectLang}"
},
{
"pattern": "packages/shared-components/src/i18n/strings/en_EN.json",
"file": "shared-components.json",
"lang": "inherited"
},
{
"group": "existing",
"pattern": "packages/shared-components/src/i18n/strings/*.json",
"file": "shared-components.json",
"excludes": ["packages/shared-components/src/i18n/strings/en_EN.json"],
"lang": "${autodetectLang}"
}
]
},
@ -27,6 +39,10 @@
{
"conditions": "equals: ${file}, element-web.json",
"output": "src/i18n/strings/${langLsrUnderscore}.json"
},
{
"conditions": "equals: ${file}, shared-components.json",
"output": "packages/shared-components/src/i18n/strings/${langLsrUnderscore}.json"
}
],
"includeSourceLang": "${includeSourceLang|false}",

View File

@ -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",
@ -69,7 +69,7 @@
"postinstall": "patch-package"
},
"resolutions": {
"**/pretty-format/react-is": "19.2.1",
"**/pretty-format/react-is": "19.2.3",
"@types/react": "19.2.7",
"@types/react-dom": "19.2.3",
"oidc-client-ts": "3.4.1",
@ -85,7 +85,7 @@
"@element-hq/web-shared-components": "link:packages/shared-components",
"@fontsource/fira-code": "^5",
"@fontsource/inter": "^5",
"@formatjs/intl-segmenter": "^11.5.7",
"@formatjs/intl-segmenter": "^12.0.0",
"@matrix-org/analytics-events": "^0.30.0",
"@matrix-org/emojibase-bindings": "^1.5.0",
"@matrix-org/react-sdk-module-api": "^2.4.0",
@ -137,7 +137,7 @@
"opus-recorder": "^8.0.3",
"pako": "^2.0.3",
"png-chunks-extract": "^1.0.0",
"posthog-js": "1.302.2",
"posthog-js": "1.313.0",
"qrcode": "1.5.4",
"re-resizable": "6.11.2",
"react": "^19.0.0",

View File

@ -0,0 +1,5 @@
// Even though this (at time of writing) is identical Element Web's
// .prettierrc.js, shared components needs its own because otherwise
// this refers to element web's copy of eslint-plugin-matrix-org which
// would require element-web's modules to be installed.
module.exports = require("eslint-plugin-matrix-org/.prettierrc.js");

View File

@ -10,16 +10,14 @@ import { WithTooltip, IconButton, TooltipLinkList } from "storybook/internal/com
import React from "react";
import { GlobeIcon } from "@storybook/icons";
// We can't import `shared/i18n.tsx` directly here.
// The storybook addon doesn't seem to benefit the vite config of storybook and we can't resolve the alias in i18n.tsx.
import json from "../../../webapp/i18n/languages.json";
const languages = Object.keys(json).filter((lang) => lang !== "default");
const languages = JSON.parse(process.env.STORYBOOK_LANGUAGES);
/**
* Returns the title of a language in the user's locale.
*/
function languageTitle(language: string): string {
return new Intl.DisplayNames([language], { type: "language", style: "short" }).of(language) || language;
const normalisedLang = language.toLowerCase().replace("_", "-");
return new Intl.DisplayNames([normalisedLang], { type: "language", style: "short" }).of(normalisedLang) || language;
}
export const languageAddon: Addon = {

View File

@ -7,12 +7,15 @@ Please see LICENSE files in the repository root for full details.
import type { StorybookConfig } from "@storybook/react-vite";
import path from "node:path";
import fs from "node:fs";
import { nodePolyfills } from "vite-plugin-node-polyfills";
import { mergeConfig } from "vite";
// Get a list of available languages so the language selector can display them at runtime
const languages = fs.readdirSync("src/i18n/strings").map((f) => f.slice(0, -5));
const config: StorybookConfig = {
stories: ["../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"],
staticDirs: ["../../../webapp"],
addons: ["@storybook/addon-docs", "@storybook/addon-designs", "@storybook/addon-a11y"],
framework: "@storybook/react-vite",
core: {
@ -29,8 +32,42 @@ const config: StorybookConfig = {
$webapp: path.resolve("../../webapp"),
},
},
// Needed for counterpart to work
plugins: [nodePolyfills({ include: ["process", "util"] })],
plugins: [
// Needed for counterpart to work
nodePolyfills({ include: ["process", "util"] }),
{
name: "language-middleware",
configureServer(server) {
server.middlewares.use((req, res, next) => {
if (req.url === "/i18n/languages.json") {
// Dynamically generate a languages.json file based on what files are available
const langJson: Record<string, string> = {};
for (const lang of languages) {
const normalizedLanguage = lang.toLowerCase().replace("_", "-");
const languageParts = normalizedLanguage.split("-");
if (languageParts.length === 2 && languageParts[0] === languageParts[1]) {
langJson[languageParts[0]] = `${lang}.json`;
} else {
langJson[normalizedLanguage] = `${lang}.json`;
}
}
res.setHeader("Content-Type", "application/json");
res.end(JSON.stringify(langJson));
} else if (req.url?.startsWith("/i18n/")) {
// Serve the individual language files, which annoyingly can't be a simple
// static dir because the directory structure in src doesn't match what
// the app requests.
const langFile = req.url.split("/").pop();
res.setHeader("Content-Type", "application/json");
fs.createReadStream(`src/i18n/strings/${langFile}`).pipe(res);
} else {
next();
}
});
},
},
],
server: {
allowedHosts: ["localhost", ".docker.internal"],
},
@ -42,5 +79,9 @@ const config: StorybookConfig = {
url: "https://element-hq.github.io/compound-web/",
},
},
env: (config) => ({
...config,
STORYBOOK_LANGUAGES: JSON.stringify(languages),
}),
};
export default config;

View File

@ -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",

View File

@ -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();
}
}

View 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 TranslationKey as _TranslationKey } from "matrix-web-i18n";
import type Translations from "../i18n/strings/en_EN.json";
declare global {
type TranslationKey = _TranslationKey<typeof Translations>;
}

View File

@ -0,0 +1,43 @@
{
"a11y": {
"seek_bar_label": "Panel posunu zvuku"
},
"action": {
"delete": "Smazat",
"dismiss": "Zavřít",
"explore_rooms": "Procházet místnosti",
"pause": "Pozastavit",
"play": "Přehrát",
"search": "Hledání"
},
"encryption": {
"pinned_identity_changed": "Identita %(displayName)s (<b>%(userId)s</b>) se změnila. <a>Další informace</a>",
"withdraw_verification_action": "Zrušit ověření"
},
"left_panel": {
"open_dial_pad": "Otevřít číselník"
},
"time": {
"about_day_ago": "před jedním dnem",
"about_hour_ago": "asi před hodinou",
"about_minute_ago": "před minutou",
"few_seconds_ago": "před pár vteřinami",
"in_about_day": "asi za den",
"in_about_hour": "asi za hodinu",
"in_about_minute": "asi za minutu",
"in_few_seconds": "za pár vteřin",
"in_n_days": "za %(num)s dní",
"in_n_hours": "za %(num)s hodin",
"in_n_minutes": "za %(num)s minut",
"n_days_ago": "před %(num)s dny",
"n_hours_ago": "před %(num)s hodinami",
"n_minutes_ago": "před %(num)s minutami"
},
"timeline": {
"m.audio": {
"audio_player": "Audio přehrávač",
"error_downloading_audio": "Chyba při stahování audia",
"unnamed_audio": "Nepojmenovaný audio soubor"
}
}
}

View File

@ -0,0 +1,43 @@
{
"a11y": {
"seek_bar_label": "Bar chwilio sain"
},
"action": {
"delete": "Dileu",
"dismiss": "Gwrthod",
"explore_rooms": "Archwilio Ystafelloedd",
"pause": "Oedi",
"play": "Chwarae",
"search": "Chwilio"
},
"encryption": {
"pinned_identity_changed": "Cafodd hunaniaeth (<b>%(userId)s</b>) %(displayName)s ei ailosod. <a>Dysgu rhagor</a>",
"withdraw_verification_action": "Tynnu'r dilysiad yn ôl"
},
"left_panel": {
"open_dial_pad": "Agor y pad deialu"
},
"time": {
"about_day_ago": "tua diwrnod yn ôl",
"about_hour_ago": "tua awr yn ol",
"about_minute_ago": "tua munud yn ôl",
"few_seconds_ago": "ychydig eiliadau yn ôl",
"in_about_day": "tua diwrnod o nawr",
"in_about_hour": "tuag awr o hyn",
"in_about_minute": "tua munud o nawr",
"in_few_seconds": "ychydig eiliadau o nawr",
"in_n_days": "%(num)s diwrnod o nawr",
"in_n_hours": "%(num)s awr o nawr",
"in_n_minutes": "%(num)s munud o nawr",
"n_days_ago": "%(num)s diwrnod yn ôl",
"n_hours_ago": "%(num)s awr yn ôl",
"n_minutes_ago": "%(num)s munud yn ôl"
},
"timeline": {
"m.audio": {
"audio_player": "Chwaraewr sain",
"error_downloading_audio": "Gwall wrth llwytho i lawrsain",
"unnamed_audio": "Sain dienw"
}
}
}

View File

@ -0,0 +1,35 @@
{
"a11y": {
"seek_bar_label": "Progressionsmarkør for lydafspiller"
},
"action": {
"delete": "Slet",
"dismiss": "Afvis",
"explore_rooms": "Udforsk rum",
"pause": "Pausér",
"play": "Afspil",
"search": "Søg"
},
"time": {
"about_day_ago": "omkring en dag siden",
"about_hour_ago": "for omkring en time siden",
"about_minute_ago": "for omkring et minut siden",
"few_seconds_ago": "for et par sekunder siden",
"in_about_day": "om cirka en dag fra nu",
"in_about_hour": "omkring en time fra nu",
"in_about_minute": "omkring et minut fra nu",
"in_few_seconds": "et par sekunder fra nu",
"in_n_days": "%(num)s dage fra nu",
"in_n_hours": "%(num)s timer fra nu",
"in_n_minutes": "%(num)s minutter fra nu",
"n_days_ago": "%(num)s dage siden",
"n_hours_ago": "%(num)s timer siden",
"n_minutes_ago": "%(num)s minutter siden"
},
"timeline": {
"m.audio": {
"error_downloading_audio": "Fejl ved download af lyd",
"unnamed_audio": "Unavngiven lyd"
}
}
}

View File

@ -0,0 +1,43 @@
{
"a11y": {
"seek_bar_label": "Audio-Suchleiste"
},
"action": {
"delete": "Löschen",
"dismiss": "Ausblenden",
"explore_rooms": "Chats erkunden",
"pause": "Pausieren",
"play": "Abspielen",
"search": "Suchen"
},
"encryption": {
"pinned_identity_changed": "%(displayName)s's (<b>%(userId)s</b>) Identität wurde zurückgesetzt. <a>Mehr erfahren </a>",
"withdraw_verification_action": "Verifizierung zurückziehen"
},
"left_panel": {
"open_dial_pad": "Wähltastatur öffnen"
},
"time": {
"about_day_ago": "vor etwa einem Tag",
"about_hour_ago": "vor etwa einer Stunde",
"about_minute_ago": "vor etwa einer Minute",
"few_seconds_ago": "vor ein paar Sekunden",
"in_about_day": "in etwa einem Tag",
"in_about_hour": "in etwa einer Stunde",
"in_about_minute": "in etwa einer Minute",
"in_few_seconds": "in ein paar Sekunden",
"in_n_days": "in %(num)s Tagen",
"in_n_hours": "in %(num)s Stunden",
"in_n_minutes": "In etwa %(num)s Minuten",
"n_days_ago": "vor %(num)s Tagen",
"n_hours_ago": "vor %(num)s Stunden",
"n_minutes_ago": "vor %(num)s Minuten"
},
"timeline": {
"m.audio": {
"audio_player": "Audio-Player",
"error_downloading_audio": "Fehler beim Herunterladen der Audiodatei",
"unnamed_audio": "Unbenannte Audiodatei"
}
}
}

View File

@ -0,0 +1,35 @@
{
"action": {
"delete": "Διαγραφή",
"dismiss": "Απόρριψη",
"explore_rooms": "Εξερευνήστε αίθουσες",
"pause": "Παύση",
"play": "Αναπαραγωγή",
"search": "Αναζήτηση"
},
"left_panel": {
"open_dial_pad": "Άνοιγμα πληκτρολογίου κλήσης"
},
"time": {
"about_day_ago": "σχεδόν μία μέρα πριν",
"about_hour_ago": "σχεδόν μία ώρα πριν",
"about_minute_ago": "σχεδόν ένα λεπτό πριν",
"few_seconds_ago": "λίγα δευτερόλεπτα πριν",
"in_about_day": "περίπου μια μέρα από τώρα",
"in_about_hour": "περίπου μία ώρα από τώρα",
"in_about_minute": "περίπου ένα λεπτό από τώρα",
"in_few_seconds": "λίγα δευτερόλεπτα από τώρα",
"in_n_days": "%(num)s μέρες από τώρα",
"in_n_hours": "%(num)s ώρες από τώρα",
"in_n_minutes": "%(num)s λεπτά από τώρα",
"n_days_ago": "%(num)s μέρες πριν",
"n_hours_ago": "%(num)s ώρες πριν",
"n_minutes_ago": "%(num)s λεπτά πριν"
},
"timeline": {
"m.audio": {
"error_downloading_audio": "Σφάλμα λήψης ήχου",
"unnamed_audio": "Ήχος χωρίς όνομα"
}
}
}

View File

@ -0,0 +1,44 @@
{
"a11y": {
"seek_bar_label": "Audio seek bar"
},
"action": {
"delete": "Delete",
"dismiss": "Dismiss",
"explore_rooms": "Explore rooms",
"pause": "Pause",
"play": "Play",
"search": "Search"
},
"left_panel": {
"open_dial_pad": "Open dial pad"
},
"room": {
"status_bar": {
"history_visible": "This room has been configured so that new members can read history. <a>Learn More</a>"
}
},
"time": {
"about_day_ago": "about a day ago",
"about_hour_ago": "about an hour ago",
"about_minute_ago": "about a minute ago",
"few_seconds_ago": "a few seconds ago",
"in_about_day": "about a day from now",
"in_about_hour": "about an hour from now",
"in_about_minute": "about a minute from now",
"in_few_seconds": "a few seconds from now",
"in_n_days": "%(num)s days from now",
"in_n_hours": "%(num)s hours from now",
"in_n_minutes": "%(num)s minutes from now",
"n_days_ago": "%(num)s days ago",
"n_hours_ago": "%(num)s hours ago",
"n_minutes_ago": "%(num)s minutes ago"
},
"timeline": {
"m.audio": {
"audio_player": "Audio player",
"error_downloading_audio": "Error downloading audio",
"unnamed_audio": "Unnamed audio"
}
}
}

View File

@ -0,0 +1,35 @@
{
"action": {
"delete": "Forigi",
"dismiss": "Rezigni",
"explore_rooms": "Esplori ĉambrojn",
"pause": "Paŭzigi",
"play": "Ludi",
"search": "Serĉi"
},
"left_panel": {
"open_dial_pad": "Malfermi ciferplaton"
},
"time": {
"about_day_ago": "antaŭ ĉirkaŭ tago",
"about_hour_ago": "antaŭ ĉirkaŭ horo",
"about_minute_ago": "antaŭ ĉirkaŭ minuto",
"few_seconds_ago": "antaŭ kelkaj sekundoj",
"in_about_day": "ĉirkaŭ tagon de nun",
"in_about_hour": "ĉirkaŭ horon de nun",
"in_about_minute": "ĉirkaŭ minuton de nun",
"in_few_seconds": "kelkajn sekundojn de nun",
"in_n_days": "%(num)s tagojn de nun",
"in_n_hours": "%(num)s horojn de nun",
"in_n_minutes": "%(num)s minutojn de nun",
"n_days_ago": "antaŭ %(num)s tagoj",
"n_hours_ago": "antaŭ %(num)s horoj",
"n_minutes_ago": "antaŭ %(num)s minutoj"
},
"timeline": {
"m.audio": {
"error_downloading_audio": "Eraris elŝuto de sondosiero",
"unnamed_audio": "Sennoma sondosiero"
}
}
}

View File

@ -0,0 +1,39 @@
{
"a11y": {
"seek_bar_label": "Barra de búsqueda de audio"
},
"action": {
"delete": "Borrar",
"dismiss": "Omitir",
"explore_rooms": "Explorar salas",
"pause": "Pausar",
"play": "Reproducir",
"search": "Buscar"
},
"left_panel": {
"open_dial_pad": "Abrir teclado numérico"
},
"time": {
"about_day_ago": "hace aprox. un día",
"about_hour_ago": "hace aprox. una hora",
"about_minute_ago": "hace aproximadamente un minuto",
"few_seconds_ago": "hace unos segundos",
"in_about_day": "dentro de un día",
"in_about_hour": "dentro de una hora",
"in_about_minute": "dentro de un minuto",
"in_few_seconds": "dentro de unos segundos",
"in_n_days": "dentro de %(num)s días",
"in_n_hours": "dentro de %(num)s horas",
"in_n_minutes": "dentro de %(num)s minutos",
"n_days_ago": "hace %(num)s días",
"n_hours_ago": "hace %(num)s horas",
"n_minutes_ago": "hace %(num)s minutos"
},
"timeline": {
"m.audio": {
"audio_player": "Reproductor de audio",
"error_downloading_audio": "Error al descargar el audio",
"unnamed_audio": "Audio sin título"
}
}
}

View File

@ -0,0 +1,48 @@
{
"a11y": {
"seek_bar_label": "Heli kerimisriba"
},
"action": {
"delete": "Kustuta",
"dismiss": "Loobu",
"explore_rooms": "Tutvu jututubadega",
"pause": "Peata",
"play": "Esita",
"search": "Otsing"
},
"encryption": {
"pinned_identity_changed": "Kasutaja %(displayName)s (<b>%(userId)s</b>) võrguidentiteet on lähtestatud. <a>Lisateave</a>",
"withdraw_verification_action": "Eemalda verifitseerimine"
},
"left_panel": {
"open_dial_pad": "Ava numbriklahvistik"
},
"room": {
"status_bar": {
"history_visible": "See jututuba on seadistatud sel viisil, et uued liikmed saavad lugeda varasemat ajalugu.<a> Lisateave</a>"
}
},
"time": {
"about_day_ago": "umbes päev tagasi",
"about_hour_ago": "umbes tund aega tagasi",
"about_minute_ago": "umbes minut tagasi",
"few_seconds_ago": "mõni sekund tagasi",
"in_about_day": "umbes päeva pärast",
"in_about_hour": "umbes tunni pärast",
"in_about_minute": "umbes minuti pärast",
"in_few_seconds": "mõne sekundi pärast",
"in_n_days": "%(num)s päeva pärast",
"in_n_hours": "%(num)s tunni pärast",
"in_n_minutes": "%(num)s minuti pärast",
"n_days_ago": "%(num)s päeva tagasi",
"n_hours_ago": "%(num)s tundi tagasi",
"n_minutes_ago": "%(num)s minutit tagasi"
},
"timeline": {
"m.audio": {
"audio_player": "Meediaesitaja",
"error_downloading_audio": "Helifaili allalaadimine ei õnnestunud",
"unnamed_audio": "Nimetu helifail"
}
}
}

View File

@ -0,0 +1,29 @@
{
"action": {
"delete": "پاک‌کردن",
"dismiss": "نادیده بگیر",
"explore_rooms": "جستجو در اتاق ها",
"pause": "متوقف‌کردن",
"play": "اجرا کردن",
"search": "جستجو"
},
"left_panel": {
"open_dial_pad": "باز کردن صفحه شماره‌گیری"
},
"time": {
"about_day_ago": "حدود یک روز قبل",
"about_hour_ago": "حدود یک ساعت قبل",
"about_minute_ago": "حدود یک دقیقه قبل",
"few_seconds_ago": "چند ثانیه قبل",
"in_about_day": "حدود یک روز دیگر",
"in_about_hour": "حدود یک ساعت دیگر",
"in_about_minute": "حدود یک دقیقه دیگر",
"in_few_seconds": "چند ثانیه دیگر",
"in_n_days": "%(num)s روز دیگر",
"in_n_hours": "%(num)s ساعت دیگر",
"in_n_minutes": "%(num)s دقیقه دیگر",
"n_days_ago": "%(num)s روز قبل",
"n_hours_ago": "%(num)s ساعت قبل",
"n_minutes_ago": "%(num)s دقیقه قبل"
}
}

View File

@ -0,0 +1,38 @@
{
"a11y": {
"seek_bar_label": "Äänen siirtymispalkki"
},
"action": {
"delete": "Poista",
"dismiss": "Hylkää",
"explore_rooms": "Selaa huoneita",
"pause": "Keskeytä",
"play": "Toista",
"search": "Haku"
},
"left_panel": {
"open_dial_pad": "Avaa näppäimistö"
},
"time": {
"about_day_ago": "noin päivä sitten",
"about_hour_ago": "noin tunti sitten",
"about_minute_ago": "noin minuutti sitten",
"few_seconds_ago": "muutama sekunti sitten",
"in_about_day": "noin päivä sitten",
"in_about_hour": "noin tunti sitten",
"in_about_minute": "noin minuutti sitten",
"in_few_seconds": "muutama sekunti sitten",
"in_n_days": "%(num)s päivää sitten",
"in_n_hours": "%(num)s tuntia sitten",
"in_n_minutes": "%(num)s minuuttia sitten",
"n_days_ago": "%(num)s päivää sitten",
"n_hours_ago": "%(num)s tuntia sitten",
"n_minutes_ago": "%(num)s minuuttia sitten"
},
"timeline": {
"m.audio": {
"error_downloading_audio": "Virhe ääntä ladattaessa",
"unnamed_audio": "Nimetön ääni"
}
}
}

View File

@ -0,0 +1,48 @@
{
"a11y": {
"seek_bar_label": "Barre de recherche audio"
},
"action": {
"delete": "Supprimer",
"dismiss": "Ignorer",
"explore_rooms": "Parcourir les salons",
"pause": "Pause",
"play": "Lecture",
"search": "Rechercher"
},
"encryption": {
"pinned_identity_changed": "L'identité de %(displayName)s (<b>%(userId)s</b>) semble avoir changé. <a>En savoir plus</a>",
"withdraw_verification_action": "Révoquer la vérification"
},
"left_panel": {
"open_dial_pad": "Ouvrir le pavé de numérotation"
},
"room": {
"status_bar": {
"history_visible": "Ce salon a été configuré afin que les nouveaux membres puissent lire l'historique.<a> En savori plus</a>"
}
},
"time": {
"about_day_ago": "il y a environ un jour",
"about_hour_ago": "il y a environ une heure",
"about_minute_ago": "il y a environ une minute",
"few_seconds_ago": "il y a quelques secondes",
"in_about_day": "dans un jour environ",
"in_about_hour": "dans une heure environ",
"in_about_minute": "dans une minute environ",
"in_few_seconds": "dans quelques secondes",
"in_n_days": "dans %(num)s jours",
"in_n_hours": "dans %(num)s heures",
"in_n_minutes": "dans %(num)s minutes",
"n_days_ago": "il y a %(num)s jours",
"n_hours_ago": "il y a %(num)s heures",
"n_minutes_ago": "il y a %(num)s minutes"
},
"timeline": {
"m.audio": {
"audio_player": "Lecteur audio",
"error_downloading_audio": "Erreur lors du téléchargement de laudio",
"unnamed_audio": "Audio sans nom"
}
}
}

View File

@ -0,0 +1,35 @@
{
"action": {
"delete": "Eliminar",
"dismiss": "Rexeitar",
"explore_rooms": "Explorar salas",
"pause": "Deter",
"play": "Reproducir",
"search": "Busca"
},
"left_panel": {
"open_dial_pad": "Abrir marcador"
},
"time": {
"about_day_ago": "onte",
"about_hour_ago": "fai unha hora",
"about_minute_ago": "fai un minuto",
"few_seconds_ago": "fai uns segundos",
"in_about_day": "foi onte",
"in_about_hour": "fará unha hora",
"in_about_minute": "haberá un minuto",
"in_few_seconds": "hai só uns segundos",
"in_n_days": "fará %(num)s días",
"in_n_hours": "fará %(num)s horas",
"in_n_minutes": "fará %(num)s minutos",
"n_days_ago": "fai %(num)s días",
"n_hours_ago": "fai %(num)s horas",
"n_minutes_ago": "fai %(num)s minutos"
},
"timeline": {
"m.audio": {
"error_downloading_audio": "Erro ao descargar o audio",
"unnamed_audio": "Audio sen nome"
}
}
}

View File

@ -0,0 +1,27 @@
{
"action": {
"delete": "מחק",
"dismiss": "התעלם",
"explore_rooms": "גלה חדרים",
"search": "חפש"
},
"left_panel": {
"open_dial_pad": "פתח לוח חיוג"
},
"time": {
"about_day_ago": "בערך לפני יום",
"about_hour_ago": "בערך לפני כשעה",
"about_minute_ago": "לפני בערך דקה",
"few_seconds_ago": "לפני מספר שניות",
"in_about_day": "בערך בעוד יום מעכשיו",
"in_about_hour": "בערך בעוד כשעה",
"in_about_minute": "בערך עוד דקה אחת",
"in_few_seconds": "בעוד מספר שניות מעכשיו",
"in_n_days": "בעוד %(num)s ימים מעכשיו",
"in_n_hours": "בעוד %(num)s שעות",
"in_n_minutes": "בעוד %(num)s דקות",
"n_days_ago": "לפני %(num)s ימים",
"n_hours_ago": "לפני %(num)s שעות",
"n_minutes_ago": "לפני %(num)s דקות"
}
}

View File

@ -0,0 +1,43 @@
{
"a11y": {
"seek_bar_label": "Hang keresősávja"
},
"action": {
"delete": "Törlés",
"dismiss": "Eltüntetés",
"explore_rooms": "Szobák felderítése",
"pause": "Szünet",
"play": "Lejátszás",
"search": "Keresés"
},
"encryption": {
"pinned_identity_changed": "Úgy tűnik, hogy %(displayName)s (<b>%(userId)s</b>) személyazonossága megváltozott. <a>További információ</a>",
"withdraw_verification_action": "Ellenőrzés visszavonása"
},
"left_panel": {
"open_dial_pad": "Számlap megnyitása"
},
"time": {
"about_day_ago": "egy napja",
"about_hour_ago": "egy órája",
"about_minute_ago": "egy perce",
"few_seconds_ago": "néhány másodperce",
"in_about_day": "egy nap múlva",
"in_about_hour": "egy óra múlva",
"in_about_minute": "egy perc múlva",
"in_few_seconds": "másodpercek múlva",
"in_n_days": "%(num)s nap múlva",
"in_n_hours": "%(num)s óra múlva",
"in_n_minutes": "%(num)s perc múlva",
"n_days_ago": "%(num)s nappal ezelőtt",
"n_hours_ago": "%(num)s órával ezelőtt",
"n_minutes_ago": "%(num)s perccel ezelőtt"
},
"timeline": {
"m.audio": {
"audio_player": "Hanglejátszó",
"error_downloading_audio": "Hiba a hang letöltésekor",
"unnamed_audio": "Névtelen hang"
}
}
}

View File

@ -0,0 +1,43 @@
{
"a11y": {
"seek_bar_label": "Աուդիո որոնման գոտի"
},
"action": {
"delete": "Ջնջել",
"dismiss": "Հեռացնել",
"explore_rooms": "Փնտրել սենյակներ",
"pause": "Դադար",
"play": "Միացնել",
"search": "Որոնել"
},
"encryption": {
"pinned_identity_changed": "%(displayName)s-ի (<b>%(userId)s</b> ) ինքնությունը վերակայվել է։ <a> Իմանալ ավելին</a>",
"withdraw_verification_action": "Հետ կանչել հաստատումը"
},
"left_panel": {
"open_dial_pad": "Բացեք թվերի հավաքման վահանակը"
},
"time": {
"about_day_ago": "մոտ մեկ օր առաջ",
"about_hour_ago": "մոտ մեկ ժամ առաջ",
"about_minute_ago": "մոտ մեկ րոպե առաջ",
"few_seconds_ago": "մի քանի վայրկյան առաջ",
"in_about_day": "մոտ մեկ օր անց",
"in_about_hour": "մոտ մեկ ժամ անց",
"in_about_minute": "մոտ մեկ րոպե անց",
"in_few_seconds": "մի քանի վայրկյան անց",
"in_n_days": "%(num)s օր անց",
"in_n_hours": "%(num)s ժամ անց",
"in_n_minutes": "%(num)s րոպեներ անց",
"n_days_ago": "%(num)s օր առաջ",
"n_hours_ago": "%(num)s ժամ առաջ",
"n_minutes_ago": "%(num)s րոպե առաջ"
},
"timeline": {
"m.audio": {
"audio_player": "Աուդիո նվագարկիչ",
"error_downloading_audio": "Աուդիո ներբեռնման սխալ",
"unnamed_audio": "Անանուն աուդիո"
}
}
}

View File

@ -0,0 +1,43 @@
{
"a11y": {
"seek_bar_label": "Bilah pencarian audio"
},
"action": {
"delete": "Hapus",
"dismiss": "Abaikan",
"explore_rooms": "Jelajahi ruangan",
"pause": "Jeda",
"play": "Mainkan",
"search": "Cari"
},
"encryption": {
"pinned_identity_changed": "Identitas (<b>%(userId)s</b>) %(displayName)s tampaknya telah berubah. <a>Pelajari lebih lanjut</a>",
"withdraw_verification_action": "Tolak verifikasi"
},
"left_panel": {
"open_dial_pad": "Buka tombol penyetel"
},
"time": {
"about_day_ago": "1 hari yang lalu",
"about_hour_ago": "1 jam yang lalu",
"about_minute_ago": "1 menit yang lalu",
"few_seconds_ago": "beberapa detik yang lalu",
"in_about_day": "1 hari dari sekarang",
"in_about_hour": "1 jam dari sekarang",
"in_about_minute": "1 menit dari sekarang",
"in_few_seconds": "beberapa detik dari sekarang",
"in_n_days": "%(num)s hari dari sekarang",
"in_n_hours": "%(num)s jam dari sekarang",
"in_n_minutes": "%(num)s dari sekarang",
"n_days_ago": "%(num)s hari yang lalu",
"n_hours_ago": "%(num)s jam yang lalu",
"n_minutes_ago": "%(num)s menit yang lalu"
},
"timeline": {
"m.audio": {
"audio_player": "Pemutar audio",
"error_downloading_audio": "Terjadi kesalahan mengunduh audio",
"unnamed_audio": "Audio tidak dinamai"
}
}
}

View File

@ -0,0 +1,35 @@
{
"action": {
"delete": "Eyða",
"dismiss": "Hunsa",
"explore_rooms": "Kanna spjallrásir",
"pause": "Bið",
"play": "Spila",
"search": "Leita"
},
"left_panel": {
"open_dial_pad": "Opna talnaborð"
},
"time": {
"about_day_ago": "fyrir um degi síðan",
"about_hour_ago": "fyrir um klukkustund síðan",
"about_minute_ago": "fyrir um það bil mínútu síðan",
"few_seconds_ago": "fyrir örfáum sekúndum síðan",
"in_about_day": "eftir um það bil einn dag",
"in_about_hour": "eftir um það bil klukkustund",
"in_about_minute": "eftir um það bil mínútu",
"in_few_seconds": "eftir nokkrar sekúndur",
"in_n_days": "eftir %(num)s daga",
"in_n_hours": "eftir %(num)s klukkustundir",
"in_n_minutes": "eftir %(num)s mínútur",
"n_days_ago": "fyrir %(num)s dögum síðan",
"n_hours_ago": "fyrir %(num)s klukkustundum síðan",
"n_minutes_ago": "fyrir %(num)s mínútum síðan"
},
"timeline": {
"m.audio": {
"error_downloading_audio": "Villa við að sækja hljóð",
"unnamed_audio": "Nafnlaust hljóð"
}
}
}

View File

@ -0,0 +1,38 @@
{
"a11y": {
"seek_bar_label": "Barra di ricerca audio"
},
"action": {
"delete": "Elimina",
"dismiss": "Chiudi",
"explore_rooms": "Esplora stanze",
"pause": "Pausa",
"play": "Riproduci",
"search": "Cerca"
},
"left_panel": {
"open_dial_pad": "Apri tastierino"
},
"time": {
"about_day_ago": "circa un giorno fa",
"about_hour_ago": "circa un'ora fa",
"about_minute_ago": "circa un minuto fa",
"few_seconds_ago": "pochi secondi fa",
"in_about_day": "circa un giorno da adesso",
"in_about_hour": "circa un'ora da adesso",
"in_about_minute": "circa un minuto da adesso",
"in_few_seconds": "pochi secondi da adesso",
"in_n_days": "%(num)s giorni da adesso",
"in_n_hours": "%(num)s ore da adesso",
"in_n_minutes": "%(num)s minuti da adesso",
"n_days_ago": "%(num)s giorni fa",
"n_hours_ago": "%(num)s ore fa",
"n_minutes_ago": "%(num)s minuti fa"
},
"timeline": {
"m.audio": {
"error_downloading_audio": "Errore di scaricamento dell'audio",
"unnamed_audio": "Audio senza nome"
}
}
}

View File

@ -0,0 +1,35 @@
{
"action": {
"delete": "削除",
"dismiss": "閉じる",
"explore_rooms": "ルームを探す",
"pause": "一時停止",
"play": "再生",
"search": "検索"
},
"left_panel": {
"open_dial_pad": "ダイヤルパッドを開く"
},
"time": {
"about_day_ago": "約1日前",
"about_hour_ago": "約1時間前",
"about_minute_ago": "約1分前",
"few_seconds_ago": "数秒前",
"in_about_day": "今から約1日前",
"in_about_hour": "今から約1時間前",
"in_about_minute": "今から約1分前",
"in_few_seconds": "今から数秒前",
"in_n_days": "今から%(num)s日前",
"in_n_hours": "今から%(num)s時間前",
"in_n_minutes": "今から%(num)s分前",
"n_days_ago": "%(num)s日前",
"n_hours_ago": "%(num)s時間前",
"n_minutes_ago": "%(num)s分前"
},
"timeline": {
"m.audio": {
"error_downloading_audio": "音声をダウンロードする際にエラーが発生しました",
"unnamed_audio": "名前のない音声"
}
}
}

View File

@ -0,0 +1,32 @@
{
"action": {
"delete": "წაშლა",
"dismiss": "დახურვა",
"explore_rooms": "ოთახების დათავლიერება",
"pause": "პაუზა",
"play": "დაკვრა",
"search": "ძიება"
},
"time": {
"about_day_ago": "დაახლოებით ერთი დღის წინ",
"about_hour_ago": "დაახლოებით ერთი საათის წინ",
"about_minute_ago": "დაახლოებით ერთი წუთის წინ",
"few_seconds_ago": "რამდენიმე წამის წინ",
"in_about_day": "დაახლოებით ერთი დღის შემდეგ",
"in_about_hour": "დაახლოებით ერთი საათის შემდეგ",
"in_about_minute": "დაახლოებით ერთი წუთის შემდეგ",
"in_few_seconds": "რამდენიმე წამის შემდეგ",
"in_n_days": "%(num)sდღეებიდან",
"in_n_hours": "%(num)sსაათის შემდეგ",
"in_n_minutes": "%(num)sწუთის შემდეგ",
"n_days_ago": "%(num)sდღის წინ",
"n_hours_ago": "%(num)sსაათის წინ",
"n_minutes_ago": "%(num)sწუთის წინ"
},
"timeline": {
"m.audio": {
"error_downloading_audio": "შეცდომა აუდიოს ჩამოტვირთვისას",
"unnamed_audio": "უსახელო აუდიო"
}
}
}

View File

@ -0,0 +1,43 @@
{
"a11y": {
"seek_bar_label": "오디오 탐색 바"
},
"action": {
"delete": "삭제",
"dismiss": "버리기",
"explore_rooms": "방 검색",
"pause": "일시중지",
"play": "재생",
"search": "찾기"
},
"encryption": {
"pinned_identity_changed": "%(displayName)s (<b>%(userId)s</b>)의 신원이 재설정되었습니다. <a>자세히 알아보기</a>",
"withdraw_verification_action": "인증 취소"
},
"left_panel": {
"open_dial_pad": "다이얼 패드 열기"
},
"time": {
"about_day_ago": "약 1일 전",
"about_hour_ago": "약 1 시간 전",
"about_minute_ago": "약 1분 전",
"few_seconds_ago": "몇 초 전",
"in_about_day": "하루 정도 후",
"in_about_hour": "지금부터 한 시간 정도 후에",
"in_about_minute": "지금부터 약 1분 후",
"in_few_seconds": "몇 초 후",
"in_n_days": "지금부터 %(num)s 일 후에",
"in_n_hours": "지금부터 %(num)s 시간 후",
"in_n_minutes": "지금부터 %(num)s분 후",
"n_days_ago": "%(num)s일 전",
"n_hours_ago": "%(num)s 시간 전",
"n_minutes_ago": "%(num)s분 전"
},
"timeline": {
"m.audio": {
"audio_player": "오디오 플레이어",
"error_downloading_audio": "오디오 다운로드 중 오류 발생",
"unnamed_audio": "이름 없는 오디오"
}
}
}

View File

@ -0,0 +1,35 @@
{
"action": {
"delete": "ລຶບ",
"dismiss": "ຍົກເລີກ",
"explore_rooms": "ການສຳຫຼວດຫ້ອງ",
"pause": "ຢຸດຊົ່ວຄາວ",
"play": "ຫຼິ້ນ",
"search": "ຊອກຫາ"
},
"left_panel": {
"open_dial_pad": "ເປີດແຜ່ນປັດ"
},
"time": {
"about_day_ago": "ປະມານຫນຶ່ງມື້ກ່ອນຫນ້ານີ້",
"about_hour_ago": "ປະມານຫນຶ່ງຊົ່ວໂມງກ່ອນຫນ້ານີ້",
"about_minute_ago": "ປະມານໜຶ່ງວິນາທີກ່ອນຫນ້ານີ້",
"few_seconds_ago": "ສອງສາມວິນາທີກ່ອນຫນ້ານີ້",
"in_about_day": "ປະມານນຶ່ງມື້ຈາກນີ້",
"in_about_hour": "ປະມານຫນຶ່ງຊົ່ວໂມງຈາກປະຈຸບັນນີ້",
"in_about_minute": "ປະມານໜຶ່ງນາທີຕໍ່ຈາກນີ້",
"in_few_seconds": "ສອງສາມວິນາທີຕໍ່ຈາກນີ້ໄປ",
"in_n_days": "%(num)s ມື້ຕໍ່ຈາກນີ້",
"in_n_hours": "%(num)s ຊົ່ວໂມງຈາກປະຈຸບັນນີ້",
"in_n_minutes": "%(num)s ນາທີຕໍ່ຈາກນີ້",
"n_days_ago": "%(num)sມື້ກ່ອນຫນ້ານີ້",
"n_hours_ago": "%(num)s ຊົ່ວໂມງກ່ອນ",
"n_minutes_ago": "%(num)s ນາທີກ່ອນ"
},
"timeline": {
"m.audio": {
"error_downloading_audio": "ເກີດຄວາມຜິດພາດໃນການດາວໂຫຼດສຽງ",
"unnamed_audio": "ສຽງບໍ່ມີຊື່"
}
}
}

View File

@ -0,0 +1,24 @@
{
"action": {
"delete": "Ištrinti",
"dismiss": "Atmesti",
"explore_rooms": "Žvalgyti kambarius",
"search": "Ieškoti"
},
"time": {
"about_day_ago": "maždaug prieš dieną",
"about_hour_ago": "maždaug prieš valandą",
"about_minute_ago": "maždaug prieš minutę",
"few_seconds_ago": "prieš kelias sekundes",
"in_about_day": "apie dieną nuo dabar",
"in_about_hour": "apie valandą nuo dabar",
"in_about_minute": "apie minutę nuo dabar",
"in_few_seconds": "keletą sekundžių nuo dabar",
"in_n_days": "%(num)s dienas(-ų) nuo dabar",
"in_n_hours": "%(num)s valandas(-ų) nuo dabar",
"in_n_minutes": "%(num)s minutes(-ų) nuo dabar",
"n_days_ago": "prieš %(num)s dienas(-ų)",
"n_hours_ago": "prieš %(num)s valandas(-ų)",
"n_minutes_ago": "prieš %(num)s minutes(-ų)"
}
}

View File

@ -0,0 +1,35 @@
{
"a11y": {
"seek_bar_label": "Audio meklēšanas josla"
},
"action": {
"delete": "Izdzēst",
"dismiss": "Atmest",
"explore_rooms": "Pārlūkot istabas",
"pause": "Pauzēt",
"play": "Atskaņot",
"search": "Meklēt"
},
"time": {
"about_day_ago": "aptuveni dienu iepriekš",
"about_hour_ago": "aptuveni stundu iepriekš",
"about_minute_ago": "aptuveni minūti iepriekš",
"few_seconds_ago": "pirms dažām sekundēm",
"in_about_day": "aptuveni dienu kopš šī brīža",
"in_about_hour": "aptuveni stundu kopš šī brīža",
"in_about_minute": "aptuveni minūti kopš šī brīža",
"in_few_seconds": "dažas sekundes kopš šī brīža",
"in_n_days": "%(num)s dienas kopš šī brīža",
"in_n_hours": "%(num)s stundas kopš šī brīža",
"in_n_minutes": "%(num)s minūtes kopš šī brīža",
"n_days_ago": "%(num)s dienas iepriekš",
"n_hours_ago": "%(num)s stundas iepriekš",
"n_minutes_ago": "%(num)s minūtes iepriekš"
},
"timeline": {
"m.audio": {
"error_downloading_audio": "Kļūda skaņas lejupielādēšanā",
"unnamed_audio": "Nenosaukts audio"
}
}
}

View File

@ -0,0 +1,38 @@
{
"a11y": {
"seek_bar_label": "Audio mitady bar"
},
"action": {
"delete": "Esorina",
"dismiss": "Hanario",
"explore_rooms": "Tsidiho ny efitrano",
"pause": "Mihato",
"play": "Milalao",
"search": "Karohina"
},
"left_panel": {
"open_dial_pad": "Sokafy ny dial pad"
},
"time": {
"about_day_ago": "Tokony ho iray andro izay",
"about_hour_ago": "Manakaiky adin'iray Teo ho eo",
"about_minute_ago": "Misy iray minitra Teo izay",
"few_seconds_ago": "Segondra vitsy lasa",
"in_about_day": "Anatiny iray andro eo ho eo",
"in_about_hour": "Adiny iray eo ho eo",
"in_about_minute": "Afaka iray minitra eo ho eo",
"in_few_seconds": "Afaka segondra vitsy",
"in_n_days": "%(num) s andro manomboka izao",
"in_n_hours": "% (num) sAnatiny ora vitsivitsy",
"in_n_minutes": "% (Num) sAfaka minitra vitsy",
"n_days_ago": "%(num)s Andro vitsivitsy izay",
"n_hours_ago": "%(num)sOra maromaro",
"n_minutes_ago": "%(Num)s Minitra vitsivitsy izay"
},
"timeline": {
"m.audio": {
"error_downloading_audio": "Hadisoana tamin'ny fampidinana feo",
"unnamed_audio": "Audio tsy voatonona anarana"
}
}
}

View File

@ -0,0 +1,43 @@
{
"a11y": {
"seek_bar_label": "Søkelinje for lyd"
},
"action": {
"delete": "Slett",
"dismiss": "Avvis",
"explore_rooms": "Se alle rom",
"pause": "Pause",
"play": "Spill av",
"search": "Søk"
},
"encryption": {
"pinned_identity_changed": "%(displayName)ss (<b>%(userId)s</b>) identitet ser ut til å ha endret seg. <a>Finn ut mer</a>",
"withdraw_verification_action": "Trekk tilbake verifisering"
},
"left_panel": {
"open_dial_pad": "Åpne nummerpanelet"
},
"time": {
"about_day_ago": "cirka 1 dag siden",
"about_hour_ago": "cirka 1 time siden",
"about_minute_ago": "cirka 1 minutt siden",
"few_seconds_ago": "noen sekunder siden",
"in_about_day": "rundt en dag fra nå",
"in_about_hour": "rundt en time fra nå",
"in_about_minute": "rundt et minutt fra nå",
"in_few_seconds": "om noen sekunder fra nå",
"in_n_days": "%(num)s dager fra nå",
"in_n_hours": "%(num)s timer fra nå",
"in_n_minutes": "%(num)s minutter fra nå",
"n_days_ago": "%(num)s dager siden",
"n_hours_ago": "%(num)s timer siden",
"n_minutes_ago": "%(num)s minutter siden"
},
"timeline": {
"m.audio": {
"audio_player": "Lydavspiller",
"error_downloading_audio": "Feil ved nedlasting av lyd",
"unnamed_audio": "Ikke navngitt lyd"
}
}
}

View File

@ -0,0 +1,38 @@
{
"a11y": {
"seek_bar_label": "Audio zoekbalk"
},
"action": {
"delete": "Verwijderen",
"dismiss": "Sluiten",
"explore_rooms": "Kamers ontdekken",
"pause": "Pauze",
"play": "Afspelen",
"search": "Zoeken"
},
"left_panel": {
"open_dial_pad": "Kiestoetsen openen"
},
"time": {
"about_day_ago": "ongeveer een dag geleden",
"about_hour_ago": "ongeveer een uur geleden",
"about_minute_ago": "ongeveer een minuut geleden",
"few_seconds_ago": "enige tellen geleden",
"in_about_day": "over een dag of zo",
"in_about_hour": "over ongeveer een uur",
"in_about_minute": "over ongeveer een minuut",
"in_few_seconds": "over een paar tellen",
"in_n_days": "over %(num)s dagen",
"in_n_hours": "over %(num)s uur",
"in_n_minutes": "over %(num)s minuten",
"n_days_ago": "%(num)s dagen geleden",
"n_hours_ago": "%(num)s uur geleden",
"n_minutes_ago": "%(num)s minuten geleden"
},
"timeline": {
"m.audio": {
"error_downloading_audio": "Fout bij downloaden van audio",
"unnamed_audio": "Naamloze audio"
}
}
}

View File

@ -0,0 +1,43 @@
{
"a11y": {
"seek_bar_label": "Pasek wyszukiwania audio"
},
"action": {
"delete": "Usuń",
"dismiss": "Pomiń",
"explore_rooms": "Przeglądaj pokoje",
"pause": "Wstrzymaj",
"play": "Odtwórz",
"search": "Szukaj"
},
"encryption": {
"pinned_identity_changed": "Tożsamość %(displayName)s (<b>%(userId)s</b>) została zresetowana. <a>Dowiedz się więcej</a>",
"withdraw_verification_action": "Wycofaj weryfikację"
},
"left_panel": {
"open_dial_pad": "Otwórz klawiaturę numeryczną"
},
"time": {
"about_day_ago": "około dzień temu",
"about_hour_ago": "około godziny temu",
"about_minute_ago": "około minuty temu",
"few_seconds_ago": "kilka sekund temu",
"in_about_day": "około dnia od teraz",
"in_about_hour": "około godziny od teraz",
"in_about_minute": "około minuty od teraz",
"in_few_seconds": "za kilka sekund",
"in_n_days": "za %(num)s dni",
"in_n_hours": "za %(num)s godzin",
"in_n_minutes": "za %(num)s minut",
"n_days_ago": "%(num)s dni temu",
"n_hours_ago": "%(num)s godzin temu",
"n_minutes_ago": "%(num)s minut temu"
},
"timeline": {
"m.audio": {
"audio_player": "Odtwarzacz audio",
"error_downloading_audio": "Wystąpił błąd w trakcie pobierania audio",
"unnamed_audio": "Audio bez nazwy"
}
}
}

View File

@ -0,0 +1,42 @@
{
"a11y": {
"seek_bar_label": "Barra de procura de áudio"
},
"action": {
"delete": "Apagar",
"dismiss": "Descartar",
"explore_rooms": "Explorar rooms",
"pause": "Pausar",
"play": "Reproduzir",
"search": "Pesquisar"
},
"encryption": {
"pinned_identity_changed": "A identidade de %(displayName)s (<b>%(userId)s</b> ) foi alterada. <a> Saber mais</a>",
"withdraw_verification_action": "Retirar verificação"
},
"left_panel": {
"open_dial_pad": "Abre o teclado de marcação"
},
"time": {
"about_day_ago": "há cerca de um dia",
"about_hour_ago": "há cerca de uma hora",
"about_minute_ago": "há cerca de um minuto",
"few_seconds_ago": "há alguns segundos atrás",
"in_about_day": "daqui a um dia",
"in_about_hour": "daqui a uma hora",
"in_about_minute": "daqui a um minuto",
"in_few_seconds": "daqui a alguns segundos",
"in_n_days": "daqui a %(num)s dias",
"in_n_hours": "daqui a %(num)s horas",
"in_n_minutes": "daqui a %(num)s minutos",
"n_days_ago": "%(num)s dias atrás",
"n_hours_ago": "%(num)s horas atrás",
"n_minutes_ago": "%(num)s minutos atrás"
},
"timeline": {
"m.audio": {
"error_downloading_audio": "Erro ao descarregar áudio",
"unnamed_audio": "Áudio sem nome"
}
}
}

View File

@ -0,0 +1,43 @@
{
"a11y": {
"seek_bar_label": "Barra de busca de áudio"
},
"action": {
"delete": "Excluir",
"dismiss": "Dispensar",
"explore_rooms": "Explorar salas",
"pause": "Pausar",
"play": "Reproduzir",
"search": "Buscar"
},
"encryption": {
"pinned_identity_changed": "A identidade de %(displayName)s (<b>%(userId)s</b>) parece ter mudado. <a>Saiba mais</a>",
"withdraw_verification_action": "Retirar verificação"
},
"left_panel": {
"open_dial_pad": "Abrir o teclado de discagem"
},
"time": {
"about_day_ago": "há aproximadamente um dia",
"about_hour_ago": "há aproximadamente uma hora",
"about_minute_ago": "há aproximadamente um minuto",
"few_seconds_ago": "há alguns segundos",
"in_about_day": "dentro de aproximadamente um dia",
"in_about_hour": "dentro de aproximadamente uma hora",
"in_about_minute": "dentro de aproximadamente um minuto",
"in_few_seconds": "dentro de alguns segundos",
"in_n_days": "dentro de %(num)s dias",
"in_n_hours": "dentro de %(num)s horas",
"in_n_minutes": "dentro de %(num)s minutos",
"n_days_ago": "há %(num)s dias",
"n_hours_ago": "há %(num)s horas",
"n_minutes_ago": "há %(num)s minutos"
},
"timeline": {
"m.audio": {
"audio_player": "Reprodutor de Áudio",
"error_downloading_audio": "Erro ao baixar o áudio",
"unnamed_audio": "Áudio sem nome"
}
}
}

View File

@ -0,0 +1,43 @@
{
"a11y": {
"seek_bar_label": "Панель поиска аудио"
},
"action": {
"delete": "Удалить",
"dismiss": "Закрыть",
"explore_rooms": "Обзор комнат",
"pause": "Пауза",
"play": "Воспроизведение",
"search": "Поиск"
},
"encryption": {
"pinned_identity_changed": "Идентичность %(displayName)s (<b>%(userId)s</b>), похоже, изменилась. <a>Узнать больше</a>",
"withdraw_verification_action": "Подтверждение верификации"
},
"left_panel": {
"open_dial_pad": "Открыть панель набора номера"
},
"time": {
"about_day_ago": "около суток назад",
"about_hour_ago": "около часа назад",
"about_minute_ago": "около минуты назад",
"few_seconds_ago": "несколько секунд назад",
"in_about_day": "примерно через день",
"in_about_hour": "примерно через час",
"in_about_minute": "примерно через минуту",
"in_few_seconds": "несколько секунд назад",
"in_n_days": "%(num)s дней спустя",
"in_n_hours": "%(num)s часов спустя",
"in_n_minutes": "%(num)s минут спустя",
"n_days_ago": "%(num)s дней назад",
"n_hours_ago": "%(num)s часов назад",
"n_minutes_ago": "%(num)s минут назад"
},
"timeline": {
"m.audio": {
"audio_player": "Аудиоплеер",
"error_downloading_audio": "Ошибка загрузки аудио",
"unnamed_audio": "Безымянное аудио"
}
}
}

View File

@ -0,0 +1,48 @@
{
"a11y": {
"seek_bar_label": "Panel vyhľadávania zvuku"
},
"action": {
"delete": "Vymazať",
"dismiss": "Zamietnuť",
"explore_rooms": "Preskúmať miestnosti",
"pause": "Pozastaviť",
"play": "Prehrať",
"search": "Hľadať"
},
"encryption": {
"pinned_identity_changed": "Zdá sa, že identita (<b>%(userId)s</b>) používateľa %(displayName)s bola obnovená. <a>Zistiť viac </a>",
"withdraw_verification_action": "Zrušiť overenie"
},
"left_panel": {
"open_dial_pad": "Otvoriť číselník"
},
"room": {
"status_bar": {
"history_visible": "Správy, ktoré odošlete, budú zdieľané s novými členmi pozvanými do tejto miestnosti. <a>Zistiť viac</a>"
}
},
"time": {
"about_day_ago": "asi pred jedným dňom",
"about_hour_ago": "približne pred hodinou",
"about_minute_ago": "približne pred minútou",
"few_seconds_ago": "pred pár sekundami",
"in_about_day": "približne o deň",
"in_about_hour": "približne o hodinu",
"in_about_minute": "približne o minútu",
"in_few_seconds": "o pár sekúnd",
"in_n_days": "o %(num)s dní",
"in_n_hours": "o %(num)s hodín",
"in_n_minutes": "o %(num)s minút",
"n_days_ago": "pred %(num)s dňami",
"n_hours_ago": "pred %(num)s hodinami",
"n_minutes_ago": "pred %(num)s minútami"
},
"timeline": {
"m.audio": {
"audio_player": "Prehrávač zvuku",
"error_downloading_audio": "Chyba pri sťahovaní zvuku",
"unnamed_audio": "Nepomenovaný zvukový záznam"
}
}
}

View File

@ -0,0 +1,35 @@
{
"action": {
"delete": "Fshije",
"dismiss": "Mos e merr parasysh",
"explore_rooms": "Eksploroni dhoma",
"pause": "Ndalesë",
"play": "Luaje",
"search": "Kërkoni"
},
"left_panel": {
"open_dial_pad": "Hap butona numrash"
},
"time": {
"about_day_ago": "rreth një ditë më parë",
"about_hour_ago": "rreth një orë më parë",
"about_minute_ago": "rreth një minutë më parë",
"few_seconds_ago": "pak sekonda më parë",
"in_about_day": "rreth një ditë nga tani",
"in_about_hour": "rreth një orë nga tani",
"in_about_minute": "rreth një minutë nga tani",
"in_few_seconds": "pak sekonda nga tani",
"in_n_days": "%(num)s ditë nga tani",
"in_n_hours": "%(num)s orë nga tani",
"in_n_minutes": "%(num)s minuta nga tani",
"n_days_ago": "%(num)s ditë më parë",
"n_hours_ago": "%(num)s orë më parë",
"n_minutes_ago": "%(num)s minuta më parë"
},
"timeline": {
"m.audio": {
"error_downloading_audio": "Gabim në shkarkim audioje",
"unnamed_audio": "Audio pa emër"
}
}
}

View File

@ -0,0 +1,43 @@
{
"a11y": {
"seek_bar_label": "Förloppsfält för ljud"
},
"action": {
"delete": "Radera",
"dismiss": "Avvisa",
"explore_rooms": "Utforska rum",
"pause": "Pausa",
"play": "Spela",
"search": "Sök"
},
"encryption": {
"pinned_identity_changed": "%(displayName)ss (<b>%(userId)s</b> ) identitet verkar ha ändrats.<a> Läs mer</a>",
"withdraw_verification_action": "Återkalla verifieringen"
},
"left_panel": {
"open_dial_pad": "Öppna knappsats"
},
"time": {
"about_day_ago": "cirka en dag sedan",
"about_hour_ago": "cirka en timme sedan",
"about_minute_ago": "cirka en minut sedan",
"few_seconds_ago": "några sekunder sedan",
"in_about_day": "om cirka en dag",
"in_about_hour": "om cirka en timme",
"in_about_minute": "om cirka en minut",
"in_few_seconds": "om några sekunder",
"in_n_days": "om %(num)s dagar",
"in_n_hours": "om %(num)s timmar",
"in_n_minutes": "om %(num)s minuter",
"n_days_ago": "%(num)s dagar sedan",
"n_hours_ago": "%(num)s timmar sedan",
"n_minutes_ago": "%(num)s minuter sedan"
},
"timeline": {
"m.audio": {
"audio_player": "Ljudspelare",
"error_downloading_audio": "Fel vid nedladdning av ljud",
"unnamed_audio": "Namnlöst ljud"
}
}
}

View File

@ -0,0 +1,42 @@
{
"a11y": {
"seek_bar_label": "Ses arama çubuğu"
},
"action": {
"delete": "Sil",
"dismiss": "Kapat",
"explore_rooms": "Odaları keşfet",
"pause": "Durdur",
"play": "Oynat",
"search": "Ara"
},
"encryption": {
"pinned_identity_changed": "%(displayName)s'ın (<b>%(userId)s</b>) kimliği değişmiş gibi görünüyor. <a>Daha fazla bilgi </a>",
"withdraw_verification_action": "Doğrulamayı iptal et"
},
"left_panel": {
"open_dial_pad": "Arama tuşlarını aç"
},
"time": {
"about_day_ago": "yaklaşık bir gün önce",
"about_hour_ago": "yaklaşık bir saat önce",
"about_minute_ago": "yaklaşık bir dakika önce",
"few_seconds_ago": "bir kaç saniye önce",
"in_about_day": "şu andan itibaren yaklaşık bir gün",
"in_about_hour": "şu andan itibaren yaklaşık bir saat",
"in_about_minute": "şu andan itibaren yaklaşık bir dakika",
"in_few_seconds": "şu andan itibaren bir kaç saniye",
"in_n_days": "şu andan itibaren %(num)s gün",
"in_n_hours": "şu andan itibaren %(num)s saat",
"in_n_minutes": "şu andan itibaren %(num)s dakika",
"n_days_ago": "%(num)s gün önce",
"n_hours_ago": "%(num)s saat önce",
"n_minutes_ago": "%(num)s dakika önce"
},
"timeline": {
"m.audio": {
"error_downloading_audio": "Ses dosyası indirilirken hata oluştu",
"unnamed_audio": "İsimsiz ses"
}
}
}

View File

@ -0,0 +1,43 @@
{
"a11y": {
"seek_bar_label": "Панель гортання аудіо"
},
"action": {
"delete": "Видалити",
"dismiss": "Відхилити",
"explore_rooms": "Каталог кімнат",
"pause": "Призупинити",
"play": "Відтворити",
"search": "Пошук"
},
"encryption": {
"pinned_identity_changed": "Ідентичність %(displayName)s (<b>%(userId)s</b>) скинуто. <a>Докладніше</a>",
"withdraw_verification_action": "Відкликати верифікацію"
},
"left_panel": {
"open_dial_pad": "Відкрити номеронабирач"
},
"time": {
"about_day_ago": "близько доби тому",
"about_hour_ago": "близько години тому",
"about_minute_ago": "близько хвилини тому",
"few_seconds_ago": "Декілька секунд тому",
"in_about_day": "приблизно через день",
"in_about_hour": "приблизно через годину",
"in_about_minute": "приблизно через хвилинку",
"in_few_seconds": "декілька секунд тому",
"in_n_days": "%(num)s днів по тому",
"in_n_hours": "%(num)s годин по тому",
"in_n_minutes": "%(num)s хвилин по тому",
"n_days_ago": "%(num)s днів тому",
"n_hours_ago": "%(num)s годин тому",
"n_minutes_ago": "%(num)s хвилин тому"
},
"timeline": {
"m.audio": {
"audio_player": "Звуковий програвач",
"error_downloading_audio": "Помилка завантаження аудіо",
"unnamed_audio": "Аудіо без назви"
}
}
}

View File

@ -0,0 +1,35 @@
{
"action": {
"delete": "Xoá",
"dismiss": "Bỏ qua",
"explore_rooms": "Khám phá các phòng",
"pause": "Tạm dừng",
"play": "Chạy",
"search": "Tìm kiếm"
},
"left_panel": {
"open_dial_pad": "Mở bàn phím quay số"
},
"time": {
"about_day_ago": "khoảng một ngày trước",
"about_hour_ago": "khoảng một giờ trước",
"about_minute_ago": "khoảng một phút trước",
"few_seconds_ago": "vài giây trước",
"in_about_day": "khoảng một ngày kể từ bây giờ",
"in_about_hour": "khoảng một giờ kể từ bây giờ",
"in_about_minute": "khoảng một phút kể từ bây giờ",
"in_few_seconds": "một vài giây kể từ bây giờ",
"in_n_days": "%(num)s ngày kể từ bây giờ",
"in_n_hours": "%(num)s giờ kể từ bây giờ",
"in_n_minutes": "%(num)s phút kể từ bây giờ",
"n_days_ago": "%(num)s ngày trước",
"n_hours_ago": "%(num)s giờ trước",
"n_minutes_ago": "%(num)s phút trước"
},
"timeline": {
"m.audio": {
"error_downloading_audio": "Lỗi khi tải xuống âm thanh",
"unnamed_audio": "Âm thanh không tên"
}
}
}

View File

@ -0,0 +1,35 @@
{
"action": {
"delete": "删除",
"dismiss": "忽略",
"explore_rooms": "查找房间",
"pause": "暂停",
"play": "播放",
"search": "搜索"
},
"left_panel": {
"open_dial_pad": "打开拨号键盘"
},
"time": {
"about_day_ago": "约一天前",
"about_hour_ago": "约一小时前",
"about_minute_ago": "约一分钟前",
"few_seconds_ago": "数秒前",
"in_about_day": "从现在开始约一天",
"in_about_hour": "从现在开始约一小时",
"in_about_minute": "从现在开始约一分钟",
"in_few_seconds": "从现在开始数秒",
"in_n_days": "从现在开始%(num)s天",
"in_n_hours": "从现在开始%(num)s小时",
"in_n_minutes": "从现在开始%(num)s分钟",
"n_days_ago": "%(num)s天前",
"n_hours_ago": "%(num)s小时前",
"n_minutes_ago": "%(num)s分钟前"
},
"timeline": {
"m.audio": {
"error_downloading_audio": "下载音频时出错",
"unnamed_audio": "未命名的音频"
}
}
}

View File

@ -0,0 +1,35 @@
{
"action": {
"delete": "刪除",
"dismiss": "關閉",
"explore_rooms": "探索聊天室",
"pause": "暫停",
"play": "播放",
"search": "搜尋"
},
"left_panel": {
"open_dial_pad": "開啟撥號鍵盤"
},
"time": {
"about_day_ago": "大約一天前",
"about_hour_ago": "大約一小時前",
"about_minute_ago": "大約一分鐘前",
"few_seconds_ago": "數秒前",
"in_about_day": "從現在開始大約一天",
"in_about_hour": "從現在開始大約一小時",
"in_about_minute": "從現在開始大約一分鐘",
"in_few_seconds": "從現在開始數秒鐘",
"in_n_days": "從現在開始 %(num)s 天",
"in_n_hours": "從現在開始 %(num)s 小時",
"in_n_minutes": "從現在開始 %(num)s 分鐘",
"n_days_ago": "%(num)s 天前",
"n_hours_ago": "%(num)s 小時前",
"n_minutes_ago": "%(num)s 分鐘前"
},
"timeline": {
"m.audio": {
"error_downloading_audio": "下載音訊時發生錯誤",
"unnamed_audio": "未命名的音訊"
}
}
}

View File

@ -36,7 +36,3 @@ export * from "./utils/I18nApi";
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";

View File

@ -8,7 +8,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 en from "../../../../src/i18n/strings/en_EN.json";
import en from "../i18n/strings/en_EN.json";
export function setupLanguageMock(): void {
fetchMock

View File

@ -5,14 +5,13 @@
* Please see LICENSE files in the repository root for full details.
*/
import { type TranslationKey } from "../i18nKeys";
import { I18nApi } from "./I18nApi";
describe("I18nApi", () => {
it("can register a translation and use it", () => {
const i18n = new I18nApi();
i18n.register({
"hello.world": {
["hello.world" as TranslationKey]: {
en: "Hello, World!",
},
});

View File

@ -9,7 +9,6 @@ import { type I18nApi as II18nApi, type Variables, type Translations } from "@el
import { humanizeTime } from "./humanize";
import { _t, getLocale, registerTranslations } from "./i18n";
import { type TranslationKey } from "../i18nKeys";
export class I18nApi implements II18nApi {
/**
@ -24,10 +23,11 @@ export class I18nApi implements II18nApi {
*/
public register(translations: Partial<Translations>): void {
const langs: Record<string, Record<string, string>> = {};
for (const key in translations) {
for (const lang in translations[key]) {
for (const lang in translations[key as keyof Translations]) {
langs[lang] = langs[lang] || {};
langs[lang][key] = translations[key][lang];
langs[lang][key] = translations[key as keyof Translations]![lang];
}
}

View File

@ -25,13 +25,11 @@ import React from "react";
import { KEY_SEPARATOR } from "matrix-web-i18n";
import counterpart from "counterpart";
import type { TranslationKey } from "../index";
// @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";
// Path where we load language files from (the index plus translations for each language)
// The filename is appended to this, so a relative path here will result in a fetch for
// a relative URL.
const i18nFolder = "i18n/";
// Control whether to also return original, untranslated strings
@ -421,13 +419,7 @@ async function getLanguage(langPath: string): Promise<ICounterpartTranslation> {
}
export async function getLangsJson(): Promise<Languages> {
let url: string;
if (typeof webpackLangJsonUrl === "string") {
// in Jest this 'url' isn't a URL, so just fall through
url = webpackLangJsonUrl;
} else {
url = i18nFolder + "languages.json";
}
const url = i18nFolder + "languages.json";
const res = await fetch(url, { method: "GET" });

File diff suppressed because it is too large Load Diff

View File

@ -647,6 +647,10 @@ test.describe("Element Call", () => {
// For this test we want to display the chat area alongside the widget
await page.getByRole("button", { name: "Chat" }).click();
// Wait for the right panel to show the timeline.
await expect(
page.locator(".mx_RightPanel .mx_TimelineCard").getByText("Alice created and configured the room."),
).toBeVisible();
await page
.locator('iframe[title="Element Call"]')
@ -654,7 +658,12 @@ test.describe("Element Call", () => {
.getByRole("button", { name: "Send Room Message" })
.click();
const messageSent = await page.getByText("I sent this once!!").count();
const timelineLocator = page.locator(".mx_RightPanel .mx_TimelineCard");
// First wait for the message to appear in the timeline then
// check the count. This improves test stability as we know the message has been sent.
await expect(timelineLocator.getByText("I sent this once!!")).toBeVisible();
const messageSent = await timelineLocator.getByText("I sent this once!!").count();
expect(messageSent).toBe(1);
});

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 27 KiB

View File

@ -10,7 +10,7 @@ import {
type StartedPostgreSqlContainer,
} from "@element-hq/element-web-playwright-common/lib/testcontainers";
const TAG = "main@sha256:2c5966c2ff06458ac5cbae959f12e19d30e3ebb63c641d31ec1ae08abccb9c6d";
const TAG = "main@sha256:48456909ce44ca9dae1a6c1abe1a2000cc2a021743d66d0b29f54077b38a09cf";
/**
* MatrixAuthenticationServiceContainer which freezes the docker digest to

View File

@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details.
import { SynapseContainer as BaseSynapseContainer } from "@element-hq/element-web-playwright-common/lib/testcontainers";
const TAG = "develop@sha256:7c3dce1d2b44fdc4b1494c5b8f4792018733ad323f823b88aac30c883d09fb35";
const TAG = "develop@sha256:106108a210f7194e0615a4d5df3502d34ee8962359b425f78d6d9d78d6b9599a";
/**
* SynapseContainer which freezes the docker digest to stabilise tests,

View File

@ -31,17 +31,25 @@ Please see LICENSE files in the repository root for full details.
.mx_EmojiPicker_anchor {
border: none;
padding: 8px 8px 6px;
padding: var(--cpd-space-1x) 0;
font-size: $font-20px;
line-height: 1;
border-bottom: 2px solid transparent;
background-color: transparent;
border-radius: 4px 4px 0 0;
width: 36px;
height: 38px;
height: 36px;
color: $primary-content;
display: inline-block;
&:not(:disabled) {
cursor: pointer;
}
&:disabled {
filter: opacity(0.3);
}
&:not(:disabled):hover {
background-color: $focus-bg-color;
@ -49,48 +57,6 @@ Please see LICENSE files in the repository root for full details.
}
}
.mx_EmojiPicker_anchor::before {
background-color: $primary-content;
content: "";
display: inline-block;
mask-size: 100%;
mask-repeat: no-repeat;
width: 100%;
height: 100%;
}
.mx_EmojiPicker_anchor:disabled::before {
background-color: $focus-bg-color;
}
.mx_EmojiPicker_anchor_activity::before {
mask-image: url("$(res)/img/emojipicker/activity.svg");
}
.mx_EmojiPicker_anchor_flags::before {
mask-image: url("$(res)/img/emojipicker/flags.svg");
}
.mx_EmojiPicker_anchor_foods::before {
mask-image: url("$(res)/img/emojipicker/foods.svg");
}
.mx_EmojiPicker_anchor_nature::before {
mask-image: url("$(res)/img/emojipicker/nature.svg");
}
.mx_EmojiPicker_anchor_objects::before {
mask-image: url("$(res)/img/emojipicker/objects.svg");
}
.mx_EmojiPicker_anchor_people::before {
mask-image: url("$(res)/img/emojipicker/people.svg");
}
.mx_EmojiPicker_anchor_places::before {
mask-image: url("$(res)/img/emojipicker/places.svg");
}
.mx_EmojiPicker_anchor_recent::before {
mask-image: url("$(res)/img/emojipicker/recent.svg");
}
.mx_EmojiPicker_anchor_symbols::before {
mask-image: url("$(res)/img/emojipicker/symbols.svg");
}
.mx_EmojiPicker_anchor_visible {
border-bottom: 2px solid $accent;
}

View File

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--
Copyright (c) 2016, Missive
From https://github.com/missive/emoji-mart/blob/master/src/svgs/index.js
Licensed under BSD-3-Clause: https://github.com/missive/emoji-mart/blob/master/LICENSE
-->
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="24"
height="24"
>
<path d="M12 0C5.373 0 0 5.372 0 12c0 6.627 5.373 12 12 12 6.628 0 12-5.373 12-12 0-6.628-5.372-12-12-12m9.949 11H17.05c.224-2.527 1.232-4.773 1.968-6.113A9.966 9.966 0 0 1 21.949 11M13 11V2.051a9.945 9.945 0 0 1 4.432 1.564c-.858 1.491-2.156 4.22-2.392 7.385H13zm-2 0H8.961c-.238-3.165-1.536-5.894-2.393-7.385A9.95 9.95 0 0 1 11 2.051V11zm0 2v8.949a9.937 9.937 0 0 1-4.432-1.564c.857-1.492 2.155-4.221 2.393-7.385H11zm4.04 0c.236 3.164 1.534 5.893 2.392 7.385A9.92 9.92 0 0 1 13 21.949V13h2.04zM4.982 4.887C5.718 6.227 6.726 8.473 6.951 11h-4.9a9.977 9.977 0 0 1 2.931-6.113M2.051 13h4.9c-.226 2.527-1.233 4.771-1.969 6.113A9.972 9.972 0 0 1 2.051 13m16.967 6.113c-.735-1.342-1.744-3.586-1.968-6.113h4.899a9.961 9.961 0 0 1-2.931 6.113" />
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--
Copyright (c) 2016, Missive
From https://github.com/missive/emoji-mart/blob/master/src/svgs/index.js
Licensed under BSD-3-Clause: https://github.com/missive/emoji-mart/blob/master/LICENSE
-->
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="24"
height="24"
>
<path d="M0 0l6.084 24H8L1.916 0zM21 5h-4l-1-4H4l3 12h3l1 4h13L21 5zM6.563 3h7.875l2 8H8.563l-2-8zm8.832 10l-2.856 1.904L12.063 13h3.332zM19 13l-1.5-6h1.938l2 8H16l3-2z" />
</svg>

Before

Width:  |  Height:  |  Size: 537 B

View File

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--
Copyright (c) 2016, Missive
From https://github.com/missive/emoji-mart/blob/master/src/svgs/index.js
Licensed under BSD-3-Clause: https://github.com/missive/emoji-mart/blob/master/LICENSE
-->
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="24"
height="24"
>
<path d="M17 4.978c-1.838 0-2.876.396-3.68.934.513-1.172 1.768-2.934 4.68-2.934a1 1 0 0 0 0-2c-2.921 0-4.629 1.365-5.547 2.512-.064.078-.119.162-.18.244C11.73 1.838 10.798.023 9.207.023 8.579.022 7.85.306 7 .978 5.027 2.54 5.329 3.902 6.492 4.999 3.609 5.222 0 7.352 0 12.969c0 4.582 4.961 11.009 9 11.009 1.975 0 2.371-.486 3-1 .629.514 1.025 1 3 1 4.039 0 9-6.418 9-11 0-5.953-4.055-8-7-8M8.242 2.546c.641-.508.943-.523.965-.523.426.169.975 1.405 1.357 3.055-1.527-.629-2.741-1.352-2.98-1.846.059-.112.241-.356.658-.686M15 21.978c-1.08 0-1.21-.109-1.559-.402l-.176-.146c-.367-.302-.816-.452-1.266-.452s-.898.15-1.266.452l-.176.146c-.347.292-.477.402-1.557.402-2.813 0-7-5.389-7-9.009 0-5.823 4.488-5.991 5-5.991 1.939 0 2.484.471 3.387 1.251l.323.276a1.995 1.995 0 0 0 2.58 0l.323-.276c.902-.78 1.447-1.251 3.387-1.251.512 0 5 .168 5 6 0 3.617-4.187 9-7 9" />
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -1,15 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--
Copyright (c) 2016, Missive
From https://github.com/missive/emoji-mart/blob/master/src/svgs/index.js
Licensed under BSD-3-Clause: https://github.com/missive/emoji-mart/blob/master/LICENSE
-->
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="24"
height="24"
>
<path d="M15.5 8a1.5 1.5 0 1 0 .001 3.001A1.5 1.5 0 0 0 15.5 8M8.5 8a1.5 1.5 0 1 0 .001 3.001A1.5 1.5 0 0 0 8.5 8" />
<path d="M18.933 0h-.027c-.97 0-2.138.787-3.018 1.497-1.274-.374-2.612-.51-3.887-.51-1.285 0-2.616.133-3.874.517C7.245.79 6.069 0 5.093 0h-.027C3.352 0 .07 2.67.002 7.026c-.039 2.479.276 4.238 1.04 5.013.254.258.882.677 1.295.882.191 3.177.922 5.238 2.536 6.38.897.637 2.187.949 3.2 1.102C8.04 20.6 8 20.795 8 21c0 1.773 2.35 3 4 3 1.648 0 4-1.227 4-3 0-.201-.038-.393-.072-.586 2.573-.385 5.435-1.877 5.925-7.587.396-.22.887-.568 1.104-.788.763-.774 1.079-2.534 1.04-5.013C23.929 2.67 20.646 0 18.933 0M3.223 9.135c-.237.281-.837 1.155-.884 1.238-.15-.41-.368-1.349-.337-3.291.051-3.281 2.478-4.972 3.091-5.031.256.015.731.27 1.265.646-1.11 1.171-2.275 2.915-2.352 5.125-.133.546-.398.858-.783 1.313M12 22c-.901 0-1.954-.693-2-1 0-.654.475-1.236 1-1.602V20a1 1 0 1 0 2 0v-.602c.524.365 1 .947 1 1.602-.046.307-1.099 1-2 1m3-3.48v.02a4.752 4.752 0 0 0-1.262-1.02c1.092-.516 2.239-1.334 2.239-2.217 0-1.842-1.781-2.195-3.977-2.195-2.196 0-3.978.354-3.978 2.195 0 .883 1.148 1.701 2.238 2.217A4.8 4.8 0 0 0 9 18.539v-.025c-1-.076-2.182-.281-2.973-.842-1.301-.92-1.838-3.045-1.853-6.478l.023-.041c.496-.826 1.49-1.45 1.804-3.102 0-2.047 1.357-3.631 2.362-4.522C9.37 3.178 10.555 3 11.948 3c1.447 0 2.685.192 3.733.57 1 .9 2.316 2.465 2.316 4.48.313 1.651 1.307 2.275 1.803 3.102.035.058.068.117.102.178-.059 5.967-1.949 7.01-4.902 7.19m6.628-8.202c-.037-.065-.074-.13-.113-.195a7.587 7.587 0 0 0-.739-.987c-.385-.455-.648-.768-.782-1.313-.076-2.209-1.241-3.954-2.353-5.124.531-.376 1.004-.63 1.261-.647.636.071 3.044 1.764 3.096 5.031.027 1.81-.347 3.218-.37 3.235" />
</svg>

Before

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -1,15 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--
Copyright (c) 2016, Missive
From https://github.com/missive/emoji-mart/blob/master/src/svgs/index.js
Licensed under BSD-3-Clause: https://github.com/missive/emoji-mart/blob/master/LICENSE
-->
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="24"
height="24"
>
<path d="M12 0a9 9 0 0 0-5 16.482V21s2.035 3 5 3 5-3 5-3v-4.518A9 9 0 0 0 12 0zm0 2c3.86 0 7 3.141 7 7s-3.14 7-7 7-7-3.141-7-7 3.14-7 7-7zM9 17.477c.94.332 1.946.523 3 .523s2.06-.19 3-.523v.834c-.91.436-1.925.689-3 .689a6.924 6.924 0 0 1-3-.69v-.833zm.236 3.07A8.854 8.854 0 0 0 12 21c.965 0 1.888-.167 2.758-.451C14.155 21.173 13.153 22 12 22c-1.102 0-2.117-.789-2.764-1.453z" />
<path d="M14.745 12.449h-.004c-.852-.024-1.188-.858-1.577-1.824-.421-1.061-.703-1.561-1.182-1.566h-.009c-.481 0-.783.497-1.235 1.537-.436.982-.801 1.811-1.636 1.791l-.276-.043c-.565-.171-.853-.691-1.284-1.794-.125-.313-.202-.632-.27-.913-.051-.213-.127-.53-.195-.634C7.067 9.004 7.039 9 6.99 9A1 1 0 0 1 7 7h.01c1.662.017 2.015 1.373 2.198 2.134.486-.981 1.304-2.058 2.797-2.075 1.531.018 2.28 1.153 2.731 2.141l.002-.008C14.944 8.424 15.327 7 16.979 7h.032A1 1 0 1 1 17 9h-.011c-.149.076-.256.474-.319.709a6.484 6.484 0 0 1-.311.951c-.429.973-.79 1.789-1.614 1.789" />
</svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -1,15 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--
Copyright (c) 2016, Missive
From https://github.com/missive/emoji-mart/blob/master/src/svgs/index.js
Licensed under BSD-3-Clause: https://github.com/missive/emoji-mart/blob/master/LICENSE
-->
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="24"
height="24"
>
<path d="M12 0C5.373 0 0 5.373 0 12s5.373 12 12 12 12-5.373 12-12S18.627 0 12 0m0 22C6.486 22 2 17.514 2 12S6.486 2 12 2s10 4.486 10 10-4.486 10-10 10" />
<path d="M8 7a2 2 0 1 0-.001 3.999A2 2 0 0 0 8 7M16 7a2 2 0 1 0-.001 3.999A2 2 0 0 0 16 7M15.232 15c-.693 1.195-1.87 2-3.349 2-1.477 0-2.655-.805-3.347-2H15m3-2H6a6 6 0 1 0 12 0" />
</svg>

Before

Width:  |  Height:  |  Size: 705 B

View File

@ -1,15 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--
Copyright (c) 2016, Missive
From https://github.com/missive/emoji-mart/blob/master/src/svgs/index.js
Licensed under BSD-3-Clause: https://github.com/missive/emoji-mart/blob/master/LICENSE
-->
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="24"
height="24"
>
<path d="M6.5 12C5.122 12 4 13.121 4 14.5S5.122 17 6.5 17 9 15.879 9 14.5 7.878 12 6.5 12m0 3c-.275 0-.5-.225-.5-.5s.225-.5.5-.5.5.225.5.5-.225.5-.5.5M17.5 12c-1.378 0-2.5 1.121-2.5 2.5s1.122 2.5 2.5 2.5 2.5-1.121 2.5-2.5-1.122-2.5-2.5-2.5m0 3c-.275 0-.5-.225-.5-.5s.225-.5.5-.5.5.225.5.5-.225.5-.5.5" />
<path d="M22.482 9.494l-1.039-.346L21.4 9h.6c.552 0 1-.439 1-.992 0-.006-.003-.008-.003-.008H23c0-1-.889-2-1.984-2h-.642l-.731-1.717C19.262 3.012 18.091 2 16.764 2H7.236C5.909 2 4.738 3.012 4.357 4.283L3.626 6h-.642C1.889 6 1 7 1 8h.003S1 8.002 1 8.008C1 8.561 1.448 9 2 9h.6l-.043.148-1.039.346a2.001 2.001 0 0 0-1.359 2.097l.751 7.508a1 1 0 0 0 .994.901H3v1c0 1.103.896 2 2 2h2c1.104 0 2-.897 2-2v-1h6v1c0 1.103.896 2 2 2h2c1.104 0 2-.897 2-2v-1h1.096a.999.999 0 0 0 .994-.901l.751-7.508a2.001 2.001 0 0 0-1.359-2.097M6.273 4.857C6.402 4.43 6.788 4 7.236 4h9.527c.448 0 .834.43.963.857L19.313 9H4.688l1.585-4.143zM7 21H5v-1h2v1zm12 0h-2v-1h2v1zm2.189-3H2.811l-.662-6.607L3 11h18l.852.393L21.189 18z" />
</svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -1,15 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--
Copyright (c) 2016, Missive
From https://github.com/missive/emoji-mart/blob/master/src/svgs/index.js
Licensed under BSD-3-Clause: https://github.com/missive/emoji-mart/blob/master/LICENSE
-->
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="24"
height="24"
>
<path d="M13 4h-2l-.001 7H9v2h2v2h2v-2h4v-2h-4z" />
<path d="M12 0C5.373 0 0 5.373 0 12s5.373 12 12 12 12-5.373 12-12S18.627 0 12 0m0 22C6.486 22 2 17.514 2 12S6.486 2 12 2s10 4.486 10 10-4.486 10-10 10" />
</svg>

Before

Width:  |  Height:  |  Size: 575 B

View File

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--
Copyright (c) 2016, Missive
From https://github.com/missive/emoji-mart/blob/master/src/svgs/index.js
Licensed under BSD-3-Clause: https://github.com/missive/emoji-mart/blob/master/LICENSE
-->
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="24"
height="24"
>
<path d="M0 0h11v2H0zM4 11h3V6h4V4H0v2h4zM15.5 17c1.381 0 2.5-1.116 2.5-2.493s-1.119-2.493-2.5-2.493S13 13.13 13 14.507 14.119 17 15.5 17m0-2.986c.276 0 .5.222.5.493 0 .272-.224.493-.5.493s-.5-.221-.5-.493.224-.493.5-.493M21.5 19.014c-1.381 0-2.5 1.116-2.5 2.493S20.119 24 21.5 24s2.5-1.116 2.5-2.493-1.119-2.493-2.5-2.493m0 2.986a.497.497 0 0 1-.5-.493c0-.271.224-.493.5-.493s.5.222.5.493a.497.497 0 0 1-.5.493M22 13l-9 9 1.513 1.5 8.99-9.009zM17 11c2.209 0 4-1.119 4-2.5V2s.985-.161 1.498.949C23.01 4.055 23 6 23 6s1-1.119 1-3.135C24-.02 21 0 21 0h-2v6.347A5.853 5.853 0 0 0 17 6c-2.209 0-4 1.119-4 2.5s1.791 2.5 4 2.5M10.297 20.482l-1.475-1.585a47.54 47.54 0 0 1-1.442 1.129c-.307-.288-.989-1.016-2.045-2.183.902-.836 1.479-1.466 1.729-1.892s.376-.871.376-1.336c0-.592-.273-1.178-.818-1.759-.546-.581-1.329-.871-2.349-.871-1.008 0-1.79.293-2.344.879-.556.587-.832 1.181-.832 1.784 0 .813.419 1.748 1.256 2.805-.847.614-1.444 1.208-1.794 1.784a3.465 3.465 0 0 0-.523 1.833c0 .857.308 1.56.924 2.107.616.549 1.423.823 2.42.823 1.173 0 2.444-.379 3.813-1.137L8.235 24h2.819l-2.09-2.383 1.333-1.135zm-6.736-6.389a1.02 1.02 0 0 1 .73-.286c.31 0 .559.085.747.254a.849.849 0 0 1 .283.659c0 .518-.419 1.112-1.257 1.784-.536-.651-.805-1.231-.805-1.742a.901.901 0 0 1 .302-.669M3.74 22c-.427 0-.778-.116-1.057-.349-.279-.232-.418-.487-.418-.766 0-.594.509-1.288 1.527-2.083.968 1.134 1.717 1.946 2.248 2.438-.921.507-1.686.76-2.3.76" />
</svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -9,8 +9,10 @@ import _ from "lodash";
import webpack from "webpack";
import type { Translations } from "matrix-web-i18n";
const I18N_BASE_PATH = "src/i18n/strings/";
const INCLUDE_LANGS = [...new Set([...fs.readdirSync(I18N_BASE_PATH)])]
const EW_I18N_BASE_PATH = "src/i18n/strings/";
const SC_I18N_BASE_PATH = "packages/shared-components/src/i18n/strings/";
const INCLUDE_LANGS = [...new Set([...fs.readdirSync(EW_I18N_BASE_PATH)])]
.filter((fn) => fn.endsWith(".json"))
.map((f) => f.slice(0, -5));
@ -41,11 +43,17 @@ const logWatch = (path: string) => {
}
};
function prepareLangFile(lang: string, dest: string): [filename: string, json: string] {
const path = I18N_BASE_PATH + lang + ".json";
/*
* Make a JSON language file for the given language by merging all translations
* into a single file (ie. element-web and shared-components).
* Returns the filename (including hash) and JSON content.
*/
function prepareLangFile(lang: string): [filename: string, json: string] {
const ewTranslationsPath = EW_I18N_BASE_PATH + lang + ".json";
const scTranslationsPath = SC_I18N_BASE_PATH + lang + ".json";
let translations: Translations = {};
[path].forEach(function (f) {
[ewTranslationsPath, scTranslationsPath].forEach(function (f) {
if (fs.existsSync(f)) {
try {
translations = _.merge(translations, JSON.parse(fs.readFileSync(f).toString()));
@ -99,7 +107,8 @@ function genLangList(langFileMap: Record<string, string>): void {
* and regenerating languages.json with the new filename
*/
function watchLanguage(lang: string, dest: string, langFileMap: Record<string, string>): void {
const path = I18N_BASE_PATH + lang + ".json";
const ewTranslationsPath = EW_I18N_BASE_PATH + lang + ".json";
const scTranslationsPath = SC_I18N_BASE_PATH + lang + ".json";
// XXX: Use a debounce because for some reason if we read the language
// file immediately after the FS event is received, the file contents
@ -110,14 +119,14 @@ function watchLanguage(lang: string, dest: string, langFileMap: Record<string, s
clearTimeout(makeLangDebouncer);
}
makeLangDebouncer = setTimeout(() => {
const [filename, json] = prepareLangFile(lang, dest);
const [filename, json] = prepareLangFile(lang);
genLangFile(dest, filename, json);
langFileMap[lang] = filename;
genLangList(langFileMap);
}, 500);
};
[path].forEach(function (f) {
[ewTranslationsPath, scTranslationsPath].forEach(function (f) {
chokidar
.watch(f, { ignoreInitial: true })
.on("ready", () => {
@ -132,7 +141,7 @@ function watchLanguage(lang: string, dest: string, langFileMap: Record<string, s
// language resources
const I18N_DEST = "webapp/i18n/";
const I18N_FILENAME_MAP = INCLUDE_LANGS.reduce<Record<string, string>>((m, l) => {
const [filename, json] = prepareLangFile(l, I18N_DEST);
const [filename, json] = prepareLangFile(l);
if (!watch) {
genLangFile(I18N_DEST, filename, json);
}

14
src/@types/i18n.d.ts vendored Normal file
View 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 TranslationKey as _TranslationKey } from "matrix-web-i18n";
import type Translations from "../i18n/strings/en_EN.json";
declare global {
type TranslationKey = _TranslationKey<typeof Translations>;
}

View File

@ -9,7 +9,7 @@ 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 { _td } from "@element-hq/web-shared-components";
import { IS_MAC, IS_ELECTRON, Key } from "../Keyboard";
import { type IBaseSetting } from "../settings/Settings";

View File

@ -12,7 +12,7 @@ import sanitizeHtml from "sanitize-html";
import classnames from "classnames";
import { logger } from "matrix-js-sdk/src/logger";
import { _t, type TranslationKey } from "../../languageHandler";
import { _t } from "../../languageHandler";
import dis from "../../dispatcher/dispatcher";
import { MatrixClientPeg } from "../../MatrixClientPeg";
import MatrixClientContext from "../../contexts/MatrixClientContext";

View File

@ -11,7 +11,7 @@ Please see LICENSE files in the repository root for full details.
import React, { type JSX } from "react";
import classNames from "classnames";
import { _t, type TranslationKey } from "../../languageHandler";
import { _t } from "../../languageHandler";
import AutoHideScrollbar from "./AutoHideScrollbar";
import { PosthogScreenTracker, type ScreenName } from "../../PosthogTrackers";
import { type NonEmptyArray } from "../../@types/common";

View File

@ -76,6 +76,13 @@ export interface IListViewProps<Item, Context> extends Omit<
onKeyDown?: (e: React.KeyboardEvent<HTMLDivElement>) => void;
}
/**
* Utility type for the prop scrollIntoViewOnChange allowing it to be memoised by a caller without repeating types
*/
export type ScrollIntoViewOnChange<Item, Context = any> = NonNullable<
VirtuosoProps<Item, ListContext<Context>>["scrollIntoViewOnChange"]
>;
/**
* A generic virtualized list component built on top of react-virtuoso.
* Provides keyboard navigation and virtualized rendering for performance with large lists.

View File

@ -15,7 +15,7 @@ import { Action } from "../../../../dispatcher/actions";
import { asyncSome } from "../../../../utils/arrays";
import { getUserDeviceIds } from "../../../../utils/crypto/deviceInfo";
import { type RoomMember } from "../../../../models/rooms/RoomMember";
import { _t, _td, type TranslationKey } from "../../../../languageHandler";
import { _t, _td } from "../../../../languageHandler";
import { E2EStatus } from "../../../../utils/ShieldUtils";
interface MemberTileViewModelProps {

View File

@ -8,7 +8,7 @@ Please see LICENSE files in the repository root for full details.
import { useCallback, useEffect, useMemo, useState } from "react";
import { FilterKey } from "../../../stores/room-list-v3/skip-list/filters";
import { _t, _td, type TranslationKey } from "../../../languageHandler";
import { _t, _td } from "../../../languageHandler";
import RoomListStoreV3, {
LISTS_LOADED_EVENT,
LISTS_UPDATE_EVENT,

View File

@ -9,7 +9,7 @@ Please see LICENSE files in the repository root for full details.
import React, { type ComponentProps, PureComponent, type Ref } from "react";
import Field, { type IInputProps } from "../elements/Field";
import { _t, _td, type TranslationKey } from "../../../languageHandler";
import { _t, _td } from "../../../languageHandler";
import withValidation, { type IFieldState, type IValidationResult } from "../elements/Validation";
import * as Email from "../../../email";

View File

@ -10,7 +10,7 @@ import React, { type ComponentProps, PureComponent, type Ref } from "react";
import Field, { type IInputProps } from "../elements/Field";
import withValidation, { type IFieldState, type IValidationResult } from "../elements/Validation";
import { _t, _td, type TranslationKey } from "../../../languageHandler";
import { _t, _td } from "../../../languageHandler";
interface IProps extends Omit<IInputProps, "onValidate" | "label" | "element"> {
id?: string;

View File

@ -12,7 +12,7 @@ import classNames from "classnames";
import type { ZxcvbnResult } from "@zxcvbn-ts/core";
import SdkConfig from "../../../SdkConfig";
import withValidation, { type IFieldState, type IValidationResult } from "../elements/Validation";
import { _t, _td, type TranslationKey } from "../../../languageHandler";
import { _t, _td } from "../../../languageHandler";
import Field, { type IInputProps } from "../elements/Field";
import { MatrixClientPeg } from "../../../MatrixClientPeg";

View File

@ -11,7 +11,7 @@ import React, { useEffect, useState } from "react";
import MediaDeviceHandler, { MediaDeviceKindEnum } from "../../../MediaDeviceHandler";
import IconizedContextMenu, { IconizedContextMenuOptionList, IconizedContextMenuRadio } from "./IconizedContextMenu";
import { type IProps as IContextMenuProps } from "../../structures/ContextMenu";
import { _t, _td, type TranslationKey } from "../../../languageHandler";
import { _t, _td } from "../../../languageHandler";
const SECTION_NAMES: Record<MediaDeviceKindEnum, TranslationKey> = {
[MediaDeviceKindEnum.AudioInput]: _td("voip|input_devices"),

View File

@ -14,7 +14,7 @@ import { sleep } from "matrix-js-sdk/src/utils";
import { logger } from "matrix-js-sdk/src/logger";
import { ErrorIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
import { _t, _td, type TranslationKey } from "../../../languageHandler";
import { _t, _td } from "../../../languageHandler";
import BaseDialog from "./BaseDialog";
import Dropdown from "../elements/Dropdown";
import SearchBox from "../../structures/SearchBox";

View File

@ -10,7 +10,7 @@ Please see LICENSE files in the repository root for full details.
import React, { type JSX, useState } from "react";
import { Form } from "@vector-im/compound-web";
import { _t, _td, type TranslationKey } from "../../../languageHandler";
import { _t, _td } from "../../../languageHandler";
import MatrixClientContext from "../../../contexts/MatrixClientContext";
import BaseDialog from "./BaseDialog";
import { TimelineEventEditor } from "./devtools/Event";

View File

@ -9,7 +9,7 @@ Please see LICENSE files in the repository root for full details.
import React, { type ChangeEvent, createRef } from "react";
import Field from "../elements/Field";
import { _t, _td, type TranslationKey } from "../../../languageHandler";
import { _t, _td } from "../../../languageHandler";
import { type IFieldState, type IValidationResult } from "../elements/Validation";
import BaseDialog from "./BaseDialog";
import DialogButtons from "../elements/DialogButtons";

View File

@ -14,7 +14,7 @@ import BaseTool, { DevtoolsContext, type IDevtoolsProps } from "./BaseTool";
import MatrixClientContext from "../../../../contexts/MatrixClientContext";
import { EventEditor, EventViewer, eventTypeField, type IEditorProps, stringify } from "./Event";
import FilteredList from "./FilteredList";
import { _td, type TranslationKey } from "../../../../languageHandler";
import { _td } from "../../../../languageHandler";
export const AccountDataEventEditor: React.FC<IEditorProps> = ({ mxEvent, onBack }) => {
const cli = useContext(MatrixClientContext);

View File

@ -11,7 +11,7 @@ import React, { createContext, type ReactNode, useState } from "react";
import { type Room } from "matrix-js-sdk/src/matrix";
import classNames from "classnames";
import { _t, type TranslationKey } from "../../../../languageHandler";
import { _t } from "../../../../languageHandler";
import { type XOR } from "../../../../@types/common";
import { type Tool } from "../DevtoolsDialog";

View File

@ -10,7 +10,7 @@ Please see LICENSE files in the repository root for full details.
import React, { type ChangeEvent, type ReactNode, useContext, useMemo, useRef, useState } from "react";
import { type IContent, type MatrixEvent, type TimelineEvents } from "matrix-js-sdk/src/matrix";
import { _t, _td, type TranslationKey } from "../../../../languageHandler";
import { _t, _td } from "../../../../languageHandler";
import Field from "../../elements/Field";
import BaseTool, { DevtoolsContext, type IDevtoolsProps } from "./BaseTool";
import MatrixClientContext from "../../../../contexts/MatrixClientContext";

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