Merge branch 'develop' into germain-gg/25884

pull/25885/head
Johannes Marbach 2023-11-22 15:41:10 +01:00 committed by GitHub
commit 1575832766
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
175 changed files with 18075 additions and 15712 deletions

View File

@ -19,7 +19,7 @@ module.exports = {
},
overrides: [
{
files: ["src/**/*.{ts,tsx}", "test/**/*.{ts,tsx}"],
files: ["src/**/*.{ts,tsx}", "test/**/*.{ts,tsx}", "scripts/*.ts"],
extends: ["plugin:matrix-org/typescript", "plugin:matrix-org/react"],
// NOTE: These rules are frozen and new rules should not be added here.
// New changes belong in https://github.com/matrix-org/eslint-plugin-matrix-org/

1
.github/CODEOWNERS vendored
View File

@ -2,3 +2,4 @@
/.github/workflows/** @vector-im/element-web-app-team
/package.json @vector-im/element-web-app-team
/yarn.lock @vector-im/element-web-app-team
/src/i18n/strings

263
.github/labels.yml vendored Normal file
View File

@ -0,0 +1,263 @@
- name: "A-Aliases"
color: "bfd4f2"
- name: "A-Authentication"
color: "bfd4f2"
- name: "A-Autocomplete"
color: "bfd4f2"
- name: "A-Breadcrumbs"
color: "bfd4f2"
- name: "A-Bridge"
color: "bfd4f2"
- name: "A-Broadcast"
description: "Broadcast-style voice messages"
color: "bfd4f2"
- name: "A-Create-Room"
description: "Create room flow, user suggestions, etc."
color: "bfd4f2"
- name: "A-DevTools"
description: "/devtools, show hidden events, etc."
color: "bfd4f2"
- name: "A-Dialogs"
color: "bfd4f2"
- name: "A-Disambiguation"
color: "bfd4f2"
- name: "A-DM-Start"
description: "Creating a DM with another user"
color: "bfd4f2"
- name: "A-E2EE-Dehydration"
color: "8CC59A"
- name: "A-Electron"
color: "bfd4f2"
- name: "A-Element-Call"
description: "Group calls via Element Call"
color: "bfd4f2"
- name: "A-Element-R"
description: "Issues affecting the port of Element's crypto layer to Rust"
color: "bfd4f2"
- name: "A-ELS"
description: "Event List Summary (and Membership ELS, MELS)"
color: "bfd4f2"
- name: "A-Emotes"
color: "bfd4f2"
- name: "A-EMS"
description: "Issues related to EMS"
color: "bfd4f2"
- name: "A-Error-Message"
color: "bfd4f2"
- name: "A-Federation"
color: "bfd4f2"
- name: "A-Feedback-Reporting"
description: "Reporting process for bugs, debug logs (rageshakes), suggestions"
color: "bfd4f2"
- name: "A-File-Download"
color: "bfd4f2"
- name: "A-File-Panel"
color: "bfd4f2"
- name: "A-Identity-Server"
color: "bfd4f2"
- name: "A-Indexing"
description: "Indexing messages via Seshat"
color: "bfd4f2"
- name: "A-IRC-Layout"
color: "bfd4f2"
- name: "A-Jump-To-Date"
description: "Jump to date headers or slash command"
color: "bfd4f2"
- name: "A-Lazy-Loading"
color: "bfd4f2"
- name: "A-Light-Box"
description: "UI when viewing an image"
color: "bfd4f2"
- name: "A-Location-Sharing"
color: "bfd4f2"
- name: "A-Logout"
description: "Logout, sign out, etc."
color: "bfd4f2"
- name: "A-Maths"
description: "Render LaTeX maths in messages"
color: "bfd4f2"
- name: "A-Memory"
description: "Memory leaks, leak hunting tools"
color: "bfd4f2"
- name: "A-Message-Forwarding"
color: "bfd4f2"
- name: "A-Message-Pinning"
color: "bfd4f2"
- name: "A-Message-Previews"
color: "bfd4f2"
- name: "A-Message-Starring"
description: "Saving favourite messages for later"
color: "bfd4f2"
- name: "A-Modules"
description: "Module system related"
color: "bfd4f2"
- name: "A-New-Search-Experience"
description: "The new search dialog available in Labs"
color: "bfd4f2"
- name: "A-Packaging"
description: "Packaging, signing, releasing"
color: "bfd4f2"
- name: "A-Peeking"
color: "bfd4f2"
- name: "A-Picture-in-Picture"
color: "bfd4f2"
- name: "A-Power-Levels"
description: "The permissions that users have in rooms and spaces"
color: "bfd4f2"
- name: "A-Replies"
description: "reply"
color: "bfd4f2"
- name: "A-Session-Mgmt"
description: "Session / device names, management UI, etc."
color: "bfd4f2"
- name: "A-Share"
color: "bfd4f2"
- name: "A-Shortcuts"
description: "Keyboard shortcuts"
color: "bfd4f2"
- name: "A-Sliding-Sync"
description: "Also known as Sync v3 - https://github.com/matrix-org/sliding-sync"
color: "bfd4f2"
- name: "A-Soft-Logout"
description: "https://github.com/vector-im/element-web/issues/10224"
color: "bfd4f2"
- name: "A-Spaces-Settings"
color: "bfd4f2"
- name: "A-SSO"
color: "bfd4f2"
- name: "A-Status-Bar"
description: "Unsent messages warning and 'Connectivity to the server has been lost'"
color: "bfd4f2"
- name: "A-Storage"
description: "Storage layer of the app, including IndexedDB, local storage, etc."
color: "bfd4f2"
- name: "A-Technical-Debt"
color: "bfd4f2"
- name: "A-Testing"
description: "Testing, code coverage, etc."
color: "bfd4f2"
- name: "A-Themes-Custom"
description: "Custom theme variables or support"
color: "bfd4f2"
- name: "A-Themes-Official"
description: "Official themes (light, dark)"
color: "bfd4f2"
- name: "A-Theming"
color: "bfd4f2"
- name: "A-Timeline-Jumpy-Scroll"
description: "Stable timeline dream ✨"
color: "bfd4f2"
- name: "A-Timesheet-1"
description: "Log any time spent on this into the A-Timesheet-1 project"
color: "5319E7"
- name: "A-Toast"
color: "bfd4f2"
- name: "A-Tooltips"
description: "Anything related to tooltips"
color: "bfd4f2"
- name: "A-UI-Customisation"
description: "UIFeatures etc. for customising entire parts of the UI"
color: "bfd4f2"
- name: "A-URL-Previews"
color: "bfd4f2"
- name: "A-User-Menu"
description: "The top left main menu with the user's name and avatar"
color: "bfd4f2"
- name: "A-User-Search"
description: "The start DM or invite to room dialogs (things dealing with `/user_directory/search`)"
color: "bfd4f2"
- name: "A-Video-Rooms"
description: "Persistent group calls"
color: "bfd4f2"
- name: "A-Voice-Messages"
color: "bfd4f2"
- name: "A-Welcome-Page"
color: "bfd4f2"
- name: "backport staging"
description: "Label to automatically backport PR to staging branch"
color: "B60205"
- name: "Dependencies"
description: "Pull requests that update a dependency file"
color: "0366d6"
- name: "Epic"
description: "Issue is at Epic level"
color: "98e6ae"
- name: "Hacktoberfest"
description: "Issues which are suitable for Hacktoberfest PRs: https://hacktoberfest.digitalocean.com/"
color: "ff7518"
- name: "P4"
description: "[OBSOLETE LABEL] Interesting — Not yet scheduled, will accept patches"
color: "d1e5f0"
- name: "spam"
color: "B60205"
- name: "Sponsored"
color: "ffc8f4"
- name: "T-Deprecation"
description: "A pull request that makes something deprecated"
color: "98e6ae"
- name: "T-Other"
description: "Questions, user support, anything else"
color: "98e6ae"
- name: "Team: App"
color: "FFA500"
- name: "X-Blocked"
color: "ff7979"
- name: "X-Cannot-Reproduce"
color: "ff7979"
- name: "X-Command"
description: "Created using the !github command"
color: "ff7979"
- name: "X-Community-Supported-Platform"
description: "This issue occurs in a platform not directly supported by us, but by a community project elsewhere"
color: "ff7979"
- name: "X-Upcoming-Release-Blocker"
description: "This does not affect the current release cycle but will affect the next one"
color: "e99695"
- name: "Z-Actions"
color: "ededed"
- name: "Z-Cache-Confusion"
description: "Related to internal cache (clearing helps / causes the issue)"
color: "ededed"
- name: "Z-Community-PR"
description: "Issue is solved by a community member's PR"
color: "ededed"
- name: "Z-Element-R-Blocker"
description: "A blocker for enabling Element R by default"
color: "ededed"
- name: "Z-Experimental"
color: "ededed"
- name: "Z-Fixed by Element Call"
description: "Issues which can be closed when we move to Element Call"
color: "ededed"
- name: "Z-Flaky-Test"
description: "A test is raising false alarms"
color: "ededed"
- name: "Z-FOSDEM"
description: "Issues in chat.fosdem.org"
color: "ededed"
- name: "Z-Gitter"
description: "Issues relating to or coming out of the Gitter migration, feature parity, etc"
color: "ededed"
- name: "Z-Legacy-Crypto"
description: "Issues affecting the legacy crypto stack"
color: "EEEEEE"
- name: "Z-Maximised-Widgets"
color: "ededed"
- name: "Z-Papercuts"
description: "Visible. Impactful. Predictable to action."
color: "ededed"
- name: "Z-Power-Users"
color: "ededed"
- name: "Z-Rageshake"
description: "Has attached rageshake (not for log submission process)"
color: "ededed"
- name: "Z-RICE"
color: "ededed"
- name: "Z-Soft-Crash"
description: "React soft crash caught by an error boundary"
color: "ededed"
- name: "Z-Spec-Compliance"
description: "An area where Element doesn't correctly implement the spec"
color: "ededed"
- name: "Z-t3chguy"
color: "ededed"

3
.github/release-drafter.yml vendored Normal file
View File

@ -0,0 +1,3 @@
_extends: matrix-org/matrix-react-sdk
version-resolver:
default: patch

View File

@ -23,7 +23,7 @@ jobs:
)
)
steps:
- uses: tibdex/backport@2e217641d82d02ba0603f46b1aeedefb258890ac # v2
- uses: tibdex/backport@9565281eda0731b1d20c4025c43339fb0a23812e # v2
with:
labels_template: "<%= JSON.stringify([...labels, 'X-Release-Blocker']) %>"
# We can't use GITHUB_TOKEN here or CI won't run on the new PR

View File

@ -2,7 +2,7 @@ name: Build
on:
pull_request: {}
push:
branches: [master]
branches: [develop, master]
# develop pushes and repository_dispatch handled in build_develop.yaml
env:
# These must be set for fetchdep.sh to get the right branch
@ -10,15 +10,35 @@ env:
PR_NUMBER: ${{ github.event.pull_request.number }}
jobs:
build:
name: "Build"
runs-on: ubuntu-latest
name: "Build on ${{ matrix.image }}"
# We build on all 3 platforms to ensure we don't have any OS-specific build incompatibilities
strategy:
fail-fast: false
matrix:
image:
- ubuntu-latest
- windows-latest
- macos-latest
isDevelop:
- ${{ github.event_name == 'push' && github.ref_name == 'develop' }}
# Skip the ubuntu-latest build for the develop branch as the dedicated CD build_develop workflow handles that
exclude:
- isDevelop: true
image: ubuntu-latest
runs-on: ${{ matrix.image }}
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
with:
cache: "yarn"
# Workaround for yarn install timeouts, especially on Windows
- run: yarn config set network-timeout 300000
- name: Install Dependencies
run: "./scripts/layered.sh"

78
.github/workflows/build_debian.yaml vendored Normal file
View File

@ -0,0 +1,78 @@
name: Build Debian package
on:
release:
types: [published]
concurrency: ${{ github.workflow }}
jobs:
build:
name: Build package
environment: packages.element.io
runs-on: ubuntu-latest
env:
R2_INCOMING_BUCKET: ${{ vars.R2_INCOMING_BUCKET }}
R2_URL: ${{ vars.CF_R2_S3_API }}
VERSION: ${{ github.ref_name }}
steps:
- uses: actions/checkout@v4
- name: Download package
run: |
wget "https://github.com/vector-im/element-web/releases/download/$VERSION/element-$VERSION.tar.gz"
wget "https://github.com/vector-im/element-web/releases/download/$VERSION/element-$VERSION.tar.gz.asc"
- name: Check GPG signature
run: |
wget "https://packages.element.io/element-release-key.gpg"
gpg --import element-release-key.gpg
gpg --fingerprint "$FINGERPRINT"
gpg --verify "element-$VERSION.tar.gz.asc" "element-$VERSION.tar.gz"
env:
FINGERPRINT: ${{ vars.GPG_FINGERPRINT }}
- name: Prepare
run: |
mkdir -p debian/tmp/DEBIAN
find debian -maxdepth 1 -type f -exec cp "{}" debian/tmp/DEBIAN/ \;
mkdir -p debian/tmp/usr/share/element-web/ debian/tmp/etc/element-web/
tar -xf "element-$VERSION.tar.gz" -C debian/tmp/usr/share/element-web --strip-components=1 --no-same-owner --no-same-permissions
mv debian/tmp/usr/share/element-web/config.sample.json debian/tmp/etc/element-web/config.json
ln -s /etc/element-web/config.json debian/tmp/usr/share/element-web/config.json
- name: Write changelog
run: |
VERSION=$(cat package.json | jq -r .version)
TIME=$(date -d "$PUBLISHED_AT" -R)
{
echo "element-web ($VERSION) default; urgency=medium"
echo "$BODY" | sed 's/^##/\n */g;s/^\*/ */g' | perl -pe 's/\[.+?]\((.+?)\)/\1/g'
echo ""
echo " -- $ACTOR <support@element.io> $TIME"
} > debian/tmp/DEBIAN/changelog
env:
ACTOR: ${{ github.actor }}
VERSION: ${{ github.event.release.tag_name }}
BODY: ${{ github.event.release.body }}
PUBLISHED_AT: ${{ github.event.release.published_at }}
- name: Build deb package
run: |
VERSION=$(cat package.json | jq -r .version)
dpkg-gencontrol -v"$VERSION" -ldebian/tmp/DEBIAN/changelog
dpkg-deb -Zxz --root-owner-group --build debian/tmp element-web.deb
- uses: actions/upload-artifact@v3
with:
name: element-web.deb
path: element-web.deb
retention-days: 14
- name: Publish to packages.element.io
if: github.event.release.prerelease == false
uses: vector-im/packages.element.io@master
with:
file: element-web.deb
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
bucket-api: ${{ vars.CF_R2_S3_API }}
bucket-key-id: ${{ secrets.CF_R2_ACCESS_KEY_ID }}
bucket-access-key: ${{ secrets.CF_R2_TOKEN }}

View File

@ -21,9 +21,9 @@ jobs:
R2_URL: ${{ vars.CF_R2_S3_API }}
R2_PUBLIC_URL: "https://element-web-develop.element.io"
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
with:
cache: "yarn"
@ -91,7 +91,7 @@ jobs:
running-workflow-name: "Build & Deploy develop.element.io"
repo-token: ${{ secrets.GITHUB_TOKEN }}
wait-interval: 10
check-regexp: ^((?!SonarCloud|SonarQube|issue|board|label).)*$
check-regexp: ^((?!SonarCloud|SonarQube|issue|board|label|Release).)*$
# We keep the latest develop.tar.gz on R2 instead of relying on the github artifact uploaded earlier
# as the expires after 24h and requires auth to download.

View File

@ -12,28 +12,41 @@ jobs:
name: Docker Buildx
runs-on: ubuntu-latest
environment: dockerhub
strategy:
fail-fast: false
matrix:
include:
- variant: vanilla
# Variant we ship to aid ESS in providing a build on the OpenCoDE platform including specific modules
- variant: opendesk
flavor: suffix=-opendesk,onlatest=true
prepare: mv variants/openDesk/* .
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 0 # needed for docker-package to be able to calculate the version
- name: Prepare
if: matrix.prepare
run: ${{ matrix.prepare }}
- name: Set up QEMU
uses: docker/setup-qemu-action@2b82ce82d56a2a04d2637cd93a637ae1b359c0a7 # v2
uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3 # v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@4c0219f9ac95b02789c1075625400b2acbff50b1 # v2
uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3
with:
install: true
- name: Login to Docker Hub
uses: docker/login-action@465a07811f14bebb1938fbed4728c6a1ff8901fc # v2
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Docker meta
id: meta
uses: docker/metadata-action@818d4b7b91585d195f67373fd9cb0332e31a7175 # v4
uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 # v5
with:
images: |
vectorim/element-web
@ -42,9 +55,10 @@ jobs:
type=ref,event=tag
flavor: |
latest=${{ contains(github.ref_name, '-rc.') && 'false' || 'auto' }}
${{ matrix.flavor }}
- name: Build and push
uses: docker/build-push-action@2eb1c1961a95fc15694676618e422e8ba1d63825 # v4
uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5
with:
context: .
push: true
@ -53,6 +67,7 @@ jobs:
labels: ${{ steps.meta.outputs.labels }}
- name: Update repo description
if: matrix.variant == 'vanilla'
uses: peter-evans/dockerhub-description@dc67fad7001ef9e8e3c124cb7a64e16d0a63d864 # v3
continue-on-error: true
with:

115
.github/workflows/docs.yml vendored Normal file
View File

@ -0,0 +1,115 @@
name: Deploy documentation
on:
push:
branches: [develop]
workflow_dispatch: {}
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: "pages"
cancel-in-progress: false
jobs:
build:
name: GitHub Pages
runs-on: ubuntu-latest
steps:
- name: Fetch element-desktop
uses: actions/checkout@v4
with:
repository: vector-im/element-desktop
path: element-desktop
- name: Fetch element-web
uses: actions/checkout@v4
with:
path: element-web
- name: Fetch matrix-react-sdk
uses: actions/checkout@v4
with:
repository: matrix-org/matrix-react-sdk
path: matrix-react-sdk
- name: Fetch matrix-js-sdk
uses: actions/checkout@v4
with:
repository: matrix-org/matrix-js-sdk
path: matrix-js-sdk
- uses: actions/setup-node@v4
with:
cache: "yarn"
cache-dependency-path: element-web/yarn.lock
- name: Generate automations docs
working-directory: element-web
run: |
yarn install --frozen-lockfile
yarn ts-node ./scripts/gen-workflow-mermaid.ts ../element-desktop ../element-web ../matrix-react-sdk ../matrix-js-sdk > docs/automations.md
echo "- [Automations](automations.md)" >> docs/SUMMARY.md
- name: Setup mdBook
uses: peaceiris/actions-mdbook@v1
with:
mdbook-version: "0.4.10"
- name: Install mdbook extensions
run: cargo install mdbook-combiner mdbook-mermaid
- name: Prepare docs
run: |
mkdir docs
mv element-desktop/README.md element-desktop/docs/
mv element-desktop/docs "docs/Element Desktop"
mv element-web/README.md element-web/docs/
mv element-web/docs/lib docs/
mv element-web/docs "docs/Element Web"
mv matrix-react-sdk/README.md matrix-react-sdk/docs/
mv matrix-react-sdk/docs "docs/Matrix React SDK"
mv matrix-js-sdk/README.md matrix-js-sdk/docs/
mv matrix-js-sdk/docs "docs/Matrix JS SDK"
sed -i -e 's/\.\.\/README.md/README.md/' docs/**/SUMMARY.md
mdbook-combiner -m docs
sed -i -E 's/^\t# (.+)$/- [\1]()/gm;t' SUMMARY.md
sed -i -E 's/^- \[(.+)]\(<>\)$/---\n# \1/gm;t' SUMMARY.md
sed -i -E 's/\t- \[Introduction]/- [Introduction]/gm;t' SUMMARY.md
cat <<EOF > docs/SUMMARY.md
# Summary
- [Introduction](<Element Web/README.md>)
EOF
cat SUMMARY.md >> docs/SUMMARY.md
mv element-web/book.toml .
- name: Build docs
run: mdbook build
- name: Upload artifact
uses: actions/upload-pages-artifact@v2
with:
path: ./book
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v2

View File

@ -9,7 +9,7 @@ jobs:
name: Tidy closed issues
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v6
- uses: actions/github-script@v7
id: main
with:
# PAT needed as the GITHUB_TOKEN won't be able to see cross-references from other orgs (matrix-org)
@ -141,7 +141,7 @@ jobs:
});
}
}
- uses: actions/github-script@v6
- uses: actions/github-script@v7
name: Close duplicate as Not Planned
if: steps.main.outputs.closeAsNotPlanned
with:

View File

@ -0,0 +1,10 @@
name: Localazy Download
on:
workflow_dispatch: {}
schedule:
- cron: "0 6 * * 1,3,5" # Every Monday, Wednesday and Friday at 6am UTC
jobs:
download:
uses: matrix-org/matrix-web-i18n/.github/workflows/localazy_download.yaml@main
secrets:
ELEMENT_BOT_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}

11
.github/workflows/localazy_upload.yaml vendored Normal file
View File

@ -0,0 +1,11 @@
name: Localazy Upload
on:
push:
branches: [develop]
paths:
- "src/i18n/strings/en_EN.json"
jobs:
upload:
uses: matrix-org/matrix-web-i18n/.github/workflows/localazy_upload.yaml@main
secrets:
LOCALAZY_WRITE_KEY: ${{ secrets.LOCALAZY_WRITE_KEY }}

View File

@ -13,7 +13,7 @@ jobs:
URL: "https://github.com/pulls?q=is%3Apr+is%3Aopen+repo%3Amatrix-org%2Fmatrix-js-sdk+repo%3Amatrix-org%2Fmatrix-react-sdk+repo%3Avector-im%2Felement-web+repo%3Avector-im%2Felement-desktop+review-requested%3A%40me+sort%3Aupdated-desc+"
RELEASE_BLOCKERS_URL: "https://github.com/pulls?q=is%3Aopen+repo%3Amatrix-org%2Fmatrix-js-sdk+repo%3Amatrix-org%2Fmatrix-react-sdk+repo%3Avector-im%2Felement-web+repo%3Avector-im%2Felement-desktop+sort%3Aupdated-desc+label%3AX-Release-Blocker+"
steps:
- uses: actions/github-script@v6
- uses: actions/github-script@v7
env:
HS_URL: ${{ secrets.BETABOT_HS_URL }}
ROOM_ID: ${{ secrets.ROOM_ID }}

21
.github/workflows/release-drafter.yml vendored Normal file
View File

@ -0,0 +1,21 @@
name: Release Drafter
on:
push:
branches: [staging]
workflow_dispatch:
inputs:
previous-version:
description: What release to use as a base for release note purposes
required: false
type: string
concurrency: ${{ github.workflow }}
jobs:
draft:
runs-on: ubuntu-latest
steps:
- uses: release-drafter/release-drafter@e64b19c4c46173209ed9f2e5a2f4ca7de89a0e86 # v5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
disable-autolabeler: true
previous-version: ${{ inputs.previous-version }}

15
.github/workflows/release-gitflow.yml vendored Normal file
View File

@ -0,0 +1,15 @@
# Gitflow merge-back master->develop
name: Merge master -> develop
on:
push:
branches: [master]
concurrency: ${{ github.repository }}-${{ github.workflow }}
jobs:
merge:
uses: matrix-org/matrix-js-sdk/.github/workflows/release-gitflow.yml@develop
secrets:
ELEMENT_BOT_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
with:
dependencies: |
matrix-react-sdk
matrix-js-sdk

39
.github/workflows/release.yml vendored Normal file
View File

@ -0,0 +1,39 @@
name: Release Process
on:
workflow_dispatch:
inputs:
mode:
description: What type of release
required: true
default: rc
type: choice
options:
- rc
- final
matrix-react-sdk:
description: React SDK version to use (current|X.Y.Z)
required: false
default: current
type: string
matrix-js-sdk:
description: JS SDK version to use (current|X.Y.Z)
required: false
default: current
type: string
concurrency: ${{ github.workflow }}
jobs:
release:
uses: matrix-org/matrix-js-sdk/.github/workflows/release-action.yml@develop
secrets:
ELEMENT_BOT_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
with:
final: ${{ inputs.mode == 'final' }}
include-changes: matrix-react-sdk
gpg-fingerprint: ${{ vars.GPG_FINGERPRINT }}
asset-path: dist/*.tar.gz
expected-asset-count: 3
dependencies: |
matrix-react-sdk=${{ inputs.matrix-react-sdk }}
matrix-js-sdk=${{ inputs.matrix-js-sdk }}

81
.github/workflows/release_prepare.yml vendored Normal file
View File

@ -0,0 +1,81 @@
name: Cut branches
on:
workflow_dispatch:
inputs:
element-desktop:
description: Prepare element-desktop
required: true
type: boolean
default: true
element-web:
description: Prepare element-web
required: true
type: boolean
default: true
matrix-react-sdk:
description: Prepare matrix-react-sdk
required: true
type: boolean
default: true
matrix-js-sdk:
description: Prepare matrix-js-sdk
required: true
type: boolean
default: true
jobs:
prepare:
runs-on: ubuntu-latest
steps:
- name: Checkout Element Desktop
uses: actions/checkout@v4
if: inputs.element-desktop
with:
repository: vector-im/element-desktop
path: element-desktop
ref: staging
fetch-depth: 0
fetch-tags: true
token: ${{ secrets.ELEMENT_BOT_TOKEN }}
- name: Checkout Element Web
uses: actions/checkout@v4
if: inputs.element-web
with:
repository: vector-im/element-web
path: element-web
ref: staging
fetch-depth: 0
fetch-tags: true
token: ${{ secrets.ELEMENT_BOT_TOKEN }}
- name: Checkout Matrix React SDK
uses: actions/checkout@v4
if: inputs.matrix-react-sdk
with:
repository: matrix-org/matrix-react-sdk
path: matrix-react-sdk
ref: staging
fetch-depth: 0
fetch-tags: true
token: ${{ secrets.ELEMENT_BOT_TOKEN }}
- name: Checkout Matrix JS SDK
uses: actions/checkout@v4
if: inputs.matrix-js-sdk
with:
repository: matrix-org/matrix-js-sdk
path: matrix-js-sdk
ref: staging
fetch-depth: 0
fetch-tags: true
token: ${{ secrets.ELEMENT_BOT_TOKEN }}
- name: Resolve repos
run: |
echo "REPOS=$(ls . | tr '\n' ' ')" >> $GITHUB_ENV
- name: Merge develop
run: |
git config --global user.email "releases@riot.im"
git config --global user.name "RiotRobot"
for REPO in $REPOS; do git -C "$REPO" merge origin/develop; done
- name: Push staging
run: for REPO in $REPOS; do git -C "$REPO" push origin staging; done

View File

@ -14,9 +14,9 @@ jobs:
name: "Typescript Syntax Check"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
with:
cache: "yarn"
@ -28,15 +28,15 @@ jobs:
i18n_lint:
name: "i18n Check"
uses: matrix-org/matrix-react-sdk/.github/workflows/i18n_check.yml@develop
uses: matrix-org/matrix-web-i18n/.github/workflows/i18n_check.yml@main
js_lint:
name: "ESLint"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
with:
cache: "yarn"
@ -51,9 +51,9 @@ jobs:
name: "Style Lint"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
with:
cache: "yarn"
@ -68,9 +68,9 @@ jobs:
name: "Analyse Dead Code"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
- uses: actions/setup-node@v4
with:
cache: "yarn"

16
.github/workflows/sync-labels.yml vendored Normal file
View File

@ -0,0 +1,16 @@
name: Sync labels
on:
workflow_dispatch: {}
push:
branches:
- develop
paths:
- .github/labels.yml
jobs:
sync-labels:
uses: vector-im/element-meta/.github/workflows/sync-labels.yml@develop
with:
LABELS: |
.github/labels.yml
secrets:
ELEMENT_BOT_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}

View File

@ -15,10 +15,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Yarn cache
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
cache: "yarn"
@ -27,7 +27,7 @@ jobs:
- name: Get number of CPU cores
id: cpu-cores
uses: SimenB/github-actions-cpu-cores@410541432439795d30db6501fb1d8178eb41e502 # v1
uses: SimenB/github-actions-cpu-cores@97ba232459a8e02ff6121db9362b09661c875ab8 # v2
- name: Run tests with coverage
run: "yarn coverage --ci --max-workers ${{ steps.cpu-cores.outputs.count }}"

View File

@ -10,7 +10,7 @@ jobs:
if: |
contains(github.event.issue.assignees.*.login, 't3chguy') ||
contains(github.event.issue.assignees.*.login, 'andybalaam') ||
contains(github.event.issue.assignees.*.login, 'justjanne')
contains(github.event.issue.assignees.*.login, 'MidhunSureshR')
steps:
- uses: actions/add-to-project@main
with:

View File

@ -3,6 +3,10 @@ name: Move labelled issues to correct projects
on:
issues:
types: [labeled]
workflow_call:
secrets:
ELEMENT_BOT_TOKEN:
required: true
jobs:
apply_Z-Labs_label:
@ -20,9 +24,10 @@ jobs:
contains(github.event.issue.labels.*.name, 'A-Video-Rooms') ||
contains(github.event.issue.labels.*.name, 'A-Message-Starring') ||
contains(github.event.issue.labels.*.name, 'A-Rich-Text-Editor') ||
contains(github.event.issue.labels.*.name, 'A-Element-Call')
contains(github.event.issue.labels.*.name, 'A-Element-Call') ||
contains(github.event.issue.labels.*.name, 'A-Element-R')
steps:
- uses: actions/github-script@v6
- uses: actions/github-script@v7
with:
script: |
github.rest.issues.addLabels({
@ -39,7 +44,7 @@ jobs:
contains(github.event.issue.labels.*.name, 'good first issue') ||
contains(github.event.issue.labels.*.name, 'Hacktoberfest')
steps:
- uses: actions/github-script@v6
- uses: actions/github-script@v7
with:
script: |
github.rest.issues.addLabels({
@ -99,45 +104,6 @@ jobs:
project-url: https://github.com/orgs/vector-im/projects/48
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
ps_features1:
name: Add labelled issues to PS features team 1
runs-on: ubuntu-latest
if: >
contains(github.event.issue.labels.*.name, 'A-Polls') ||
contains(github.event.issue.labels.*.name, 'A-Location-Sharing') ||
(contains(github.event.issue.labels.*.name, 'A-Voice-Messages') &&
!contains(github.event.issue.labels.*.name, 'A-Broadcast')) ||
(contains(github.event.issue.labels.*.name, 'A-Session-Mgmt') &&
contains(github.event.issue.labels.*.name, 'A-User-Settings'))
steps:
- uses: actions/add-to-project@main
with:
project-url: https://github.com/orgs/vector-im/projects/56
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
ps_features2:
name: Add labelled issues to PS features team 2
runs-on: ubuntu-latest
if: >
contains(github.event.issue.labels.*.name, 'A-DM-Start') ||
contains(github.event.issue.labels.*.name, 'A-Broadcast')
steps:
- uses: actions/add-to-project@main
with:
project-url: https://github.com/orgs/vector-im/projects/58
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
ps_features3:
name: Add labelled issues to PS features team 3
runs-on: ubuntu-latest
if: >
contains(github.event.issue.labels.*.name, 'A-Rich-Text-Editor')
steps:
- uses: actions/add-to-project@main
with:
project-url: https://github.com/orgs/vector-im/projects/57
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
voip:
name: Add labelled issues to VoIP project board
runs-on: ubuntu-latest
@ -159,3 +125,42 @@ jobs:
with:
project-url: https://github.com/orgs/vector-im/projects/57
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
tech_debt:
name: Add labelled issues to tech debt project
runs-on: ubuntu-latest
if: >
contains(github.event.issue.labels.*.name, 'A-Developer-Experience') ||
contains(github.event.issue.labels.*.name, 'A-Documentation') ||
contains(github.event.issue.labels.*.name, 'A-Packaging') ||
contains(github.event.issue.labels.*.name, 'A-Technical-Debt') ||
contains(github.event.issue.labels.*.name, 'A-Testing') ||
contains(github.event.issue.labels.*.name, 'Z-Flaky-Test')
steps:
- uses: actions/add-to-project@main
with:
project-url: https://github.com/orgs/vector-im/projects/101
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
element_r:
name: Add Element R issues to Crypto Team board
runs-on: ubuntu-latest
if: >
contains(github.event.issue.labels.*.name, 'A-Element-R')
steps:
- id: add_to_project
uses: actions/add-to-project@v0.5.0
with:
project-url: ${{ env.PROJECT_URL }}
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
- id: set_fields
uses: titoportas/update-project-fields@421a54430b3cdc9eefd8f14f9ce0142ab7678751 # v0.1.0
with:
project-url: ${{ env.PROJECT_URL }}
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
item-id: ${{ steps.add_to_project.outputs.itemId }} # Use the item-id output of the previous step
field-keys: Workstream,module
field-values: Element-R,web
env:
PROJECT_URL: https://github.com/orgs/vector-im/projects/76

View File

@ -60,7 +60,7 @@ jobs:
contains(github.event.issue.labels.*.name, 'A-Element-Call')) &&
contains(github.event.issue.labels.*.name, 'Z-Labs')
steps:
- uses: actions/github-script@v6
- uses: actions/github-script@v7
with:
script: |
github.rest.issues.removeLabel({

View File

@ -21,7 +21,7 @@ jobs:
runs-on: ubuntu-latest
environment: Matrix
steps:
- uses: actions/github-script@v6
- uses: actions/github-script@v7
env:
HS_URL: ${{ secrets.BETABOT_HS_URL }}
LOBBY_ROOM_ID: ${{ secrets.ROOM_ID }}
@ -62,7 +62,14 @@ jobs:
headers,
});
if (!res.ok) {
console.log(roomId, "failed to fetch", await res.text());
return;
}
const data = await res.json();
console.log(roomId, "got event", data);
const topic = data.topic.replace(regex, releaseTopic);
if (topic === data.topic) {
console.log(roomId, "nothing to do");

View File

@ -1,8 +0,0 @@
name: Upgrade Dependencies
on:
workflow_dispatch: {}
jobs:
upgrade:
uses: matrix-org/matrix-js-sdk/.github/workflows/upgrade_dependencies.yml@develop
secrets:
ELEMENT_BOT_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}

2
.gitignore vendored
View File

@ -27,3 +27,5 @@ electron/pub
# Auto-generated file
/src/modules.ts
/build_config.yaml
/book
/index.html

View File

@ -22,6 +22,9 @@ electron/pub
# Raises an error because it contains a template var breaking the script tag
src/vector/index.html
src/vector/modernizr.js
/docs/lib
/book
/debian/tmp
# This file is owned, parsed, and generated by allchange, which doesn't comply with prettier
/CHANGELOG.md

17422
CHANGELOG.md

File diff suppressed because it is too large Load Diff

View File

@ -204,7 +204,7 @@ and we'll try to fix it :)
In order to have a concrete record that your contribution is intentional
and you agree to license it under the same terms as the project's license, we've
adopted the same lightweight approach that the Linux Kernel
(https://www.kernel.org/doc/Documentation/SubmittingPatches), Docker
(https://www.kernel.org/doc/html/latest/process/submitting-patches.html), Docker
(https://github.com/docker/docker/blob/master/CONTRIBUTING.md), and many other
projects use: the DCO (Developer Certificate of Origin:
http://developercertificate.org/). This is a simple declaration that you wrote

View File

@ -1,7 +1,7 @@
[![Chat](https://img.shields.io/matrix/element-web:matrix.org?logo=matrix)](https://matrix.to/#/#element-web:matrix.org)
![Tests](https://github.com/vector-im/element-web/actions/workflows/tests.yaml/badge.svg)
![Static Analysis](https://github.com/vector-im/element-web/actions/workflows/static_analysis.yaml/badge.svg)
[![Weblate](https://translate.element.io/widgets/element-web/-/element-web/svg-badge.svg)](https://translate.element.io/engage/element-web/)
[![Localazy](https://img.shields.io/endpoint?url=https%3A%2F%2Fconnect.localazy.com%2Fstatus%2Felement-web%2Fdata%3Fcontent%3Dall%26title%3Dlocalazy%26logo%3Dtrue)](https://localazy.com/p/element-web)
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=element-web&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=element-web)
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=element-web&metric=coverage)](https://sonarcloud.io/summary/new_code?id=element-web)
[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=element-web&metric=vulnerabilities)](https://sonarcloud.io/summary/new_code?id=element-web)
@ -41,29 +41,9 @@ The easiest way to test Element is to just use the hosted copy at <https://app.e
The `develop` branch is continuously deployed to <https://develop.element.io>
for those who like living dangerously.
To host your own copy of Element, the quickest bet is to use a pre-built
released version of Element:
To host your own instance of Element see [Installing Element Web](docs/install.md).
1. Download the latest version from <https://github.com/vector-im/element-web/releases>
1. Untar the tarball on your web server
1. Move (or symlink) the `element-x.x.x` directory to an appropriate name
1. Configure the correct caching headers in your webserver (see below)
1. Configure the app by copying `config.sample.json` to `config.json` and
modifying it. See the [configuration docs](docs/config.md) for details.
1. Enter the URL into your browser and log into Element!
Releases are signed using gpg and the OpenPGP standard, and can be checked against the public key located
at <https://packages.riot.im/element-release-key.asc>.
Note that for the security of your chats will need to serve Element
over HTTPS. Major browsers also do not allow you to use VoIP/video
chats over HTTP, as WebRTC is only usable over HTTPS.
There are some exceptions like when using localhost, which is
considered a [secure context](https://developer.mozilla.org/docs/Web/Security/Secure_Contexts)
and thus allowed.
To install Element as a desktop application, see [Running as a desktop
app](#running-as-a-desktop-app) below.
To install Element as a desktop application, see [Running as a desktop app](#running-as-a-desktop-app) below.
# Important Security Notes
@ -161,61 +141,7 @@ To build it yourself, follow the instructions at <https://github.com/vector-im/e
Many thanks to @aviraldg for the initial work on the Electron integration.
Other options for running as a desktop app:
- @asdf:matrix.org points out that you can use nativefier and it just works(tm)
```bash
yarn global add nativefier
nativefier https://app.element.io/
```
The [configuration docs](docs/config.md#desktop-app-configuration) show how to
override the desktop app's default settings if desired.
# Running from Docker
The Docker image can be used to serve element-web as a web server. The easiest way to use
it is to use the prebuilt image:
```bash
docker run -p 80:80 vectorim/element-web
```
To supply your own custom `config.json`, map a volume to `/app/config.json`. For example,
if your custom config was located at `/etc/element-web/config.json` then your Docker command
would be:
```bash
docker run -p 80:80 -v /etc/element-web/config.json:/app/config.json vectorim/element-web
```
To build the image yourself:
```bash
git clone https://github.com/vector-im/element-web.git element-web
cd element-web
git checkout master
docker build .
```
If you're building a custom branch, or want to use the develop branch, check out the appropriate
element-web branch and then run:
```bash
docker build -t \
--build-arg USE_CUSTOM_SDKS=true \
--build-arg REACT_SDK_REPO="https://github.com/matrix-org/matrix-react-sdk.git" \
--build-arg REACT_SDK_BRANCH="develop" \
--build-arg JS_SDK_REPO="https://github.com/matrix-org/matrix-js-sdk.git" \
--build-arg JS_SDK_BRANCH="develop" \
.
```
# Running in Kubernetes
The provided element-web docker image can also be run from within a Kubernetes cluster.
See the [Kubernetes example](docs/kubernetes.md) for more details.
The [configuration docs](docs/config.md#desktop-app-configuration) show how to override the desktop app's default settings if desired.
# config.json
@ -388,8 +314,6 @@ To add a new translation, head to the [translating doc](docs/translating.md).
For a developer guide, see the [translating dev doc](docs/translating-dev.md).
[<img src="https://translate.element.io/widgets/element-web/-/multi-auto.svg" alt="translationsstatus" width="340">](https://translate.element.io/engage/element-web/?utm_source=widget)
# Triaging issues
Issues are triaged by community members and the Web App Team, following the [triage process](https://github.com/vector-im/element-meta/wiki/Triage-process).

33
book.toml Normal file
View File

@ -0,0 +1,33 @@
# Documentation for possible options in this file is at
# https://rust-lang.github.io/mdBook/format/config.html
[book]
title = "Element Web & Desktop"
authors = ["New Vector Ltd.", "The Matrix.org Foundation C.I.C."]
language = "en"
multilingual = false
# The directory that documentation files are stored in
src = "docs"
[build]
# Prevent markdown pages from being automatically generated when they're
# linked to in SUMMARY.md
create-missing = false
[output.html]
# Remove the numbers that appear before each item in the sidebar, as they can
# get quite messy as we nest deeper
no-section-label = true
additional-css = ["docs/lib/custom.css"]
# The source code URL of the repository
git-repository-url = "https://github.com/vector-im/element-web"
# The path that the docs are hosted on
site-url = "/element-web/"
additional-js = ["docs/lib/mermaid.min.js", "docs/lib/mermaid-init.js"]
[preprocessor]
[preprocessor.mermaid]
command = "mdbook-mermaid"

View File

@ -113,15 +113,17 @@ Unless otherwise specified, the following applies to all code:
}
```
14. Explicitly cast to a boolean, rather than relying on implicit truthiness of non-boolean values:
14. If a variable's type should be boolean, make sure it really is one.
```typescript
const isRealUser = !!userId && ...;
// ... or ...
const isRealUser = Boolean(userId) && ...;
const isRealUser = !!userId && ...; // good
const isRealUser = Boolean(userId) && Boolean(userName); // also good
const isRealUser = Boolean(userId) && isReal; // also good (where isReal is another boolean variable)
const isRealUser = Boolean(userId && userName); // also fine
const isRealUser = Boolean(userId || userName); // good: same as &&
const isRealUser = userId && ...; // bad: isRealUser is userId's type, not a boolean
// but *not*:
const isRealUser = userId && ...; // invalid implicit cast
if (userId) // fine: userId is evaluated for truthiness, not stored as a boolean
```
15. Use `switch` statements when checking against more than a few enum-like values.

2
debian/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/files
/tmp

1
debian/conffiles vendored Normal file
View File

@ -0,0 +1 @@
/etc/element-web/config.json

13
debian/control vendored Executable file
View File

@ -0,0 +1,13 @@
Source: element-web
Maintainer: support@element.io
Section: web
Priority: optional
Homepage: https://element.io/
Package: element-web
Architecture: all
Recommends: httpd, element-io-archive-keyring
Description:
A feature-rich client for Matrix.org
This package contains the web-based client that can be served through a web
server.

41
docs/SUMMARY.md Normal file
View File

@ -0,0 +1,41 @@
# Summary
- [Introduction](../README.md)
# Usage
- [Betas](betas.md)
- [Labs](labs.md)
# Setup
- [Install](install.md)
- [Config](config.md)
- [Custom home page](custom-home.md)
- [Kubernetes](kubernetes.md)
- [Jitsi](jitsi.md)
- [Encryption](e2ee.md)
# Build
- [Customisations](customisations.md)
- [Modules](modules.md)
- [Native Node modules](native-node-modules.md)
# Contribution
- [Choosing an issue](choosing-an-issue.md)
- [Translation](translating.md)
- [Netlify builds](pr-previews.md)
- [Code review](review.md)
# Development
- [App load order](app-load.md)
- [Translation](translating-dev.md)
- [Theming](theming.md)
- [Memory profiling](memory-profiles-and-leaks.md)
- [Jitsi](jitsi-dev.md)
- [Feature flags](feature-flags.md)
- [OIDC and delegated authentication](oidc.md)
- [Release Process](release.md)

View File

@ -4,78 +4,67 @@
been kept untouched for posterity.
Old slow flow:
![image](https://user-images.githubusercontent.com/2403652/73848963-00a2a080-4821-11ea-97d4-1200fc2638f3.png)
```mermaid
flowchart TD
A1(((load_modernizr))) --> B
A2((rageshake)) --> B
B(((skin))) --> C
C(((olm))) --> D
D{mobile} --> E
E((config)) --> F
F((i18n)) --> G
style F stroke:lime
G(((theme))) --> H
H(((modernizr))) --> app
style H stroke:red
```
Current more parallel flow:
![image](https://user-images.githubusercontent.com/2403652/83146440-303a2900-a0ee-11ea-806b-4f53f039b957.png)
<details><summary>Code</summary>
<p>
<pre><code>
digraph G {
node [shape=box];
```mermaid
flowchart TD
subgraph index.ts
style index.ts stroke:orange
subgraph cluster_0 {
color=orange;
node [style=filled];
label = "index.ts";
A[/rageshake/] --> B{mobile}
B-- No -->C1(.)
B-- Yes -->C2((redirect))
C1 --> D[/olm/] --> R
C1 --> E[platform] --> F[/config/]
F --> G1[/skin/]
F --> R
G1 --> H
G1 --> R
F --> G2[/theme/]
G2 --> H
G2 --> R
F --> G3[/i18n/]
G3 --> H
G3 --> R
H{modernizr}-- No --> J((incompatible))-- user ignore --> R
H-- Yes --> R
entrypoint, s0, ready [shape=point];
rageshake, config, i18n, theme, skin, olm [shape=parallelogram];
mobile [shape=diamond, label="mobile"];
modernizr [shape=diamond];
redirect, incompatible [shape=egg];
linkStyle 0,7,9,11,12,14,15 stroke:blue;
linkStyle 4,8,10,13,16 stroke:red;
end
entrypoint -> rageshake;
rageshake -> mobile [color=blue];
mobile -> s0 [label="No"];
mobile -> redirect [label="Yes"];
R>ready] --> 2A
style R stroke:gray
s0 -> platform;
s0 -> olm;
platform -> config;
subgraph init.tsx
style init.tsx stroke:lime
2A[loadApp] --> 2B[matrixchat]
end
config -> i18n [color=blue];
config -> theme [color=blue];
config -> skin [color=blue];
i18n -> modernizr [color=blue];
theme -> modernizr [color=blue];
skin -> modernizr [color=blue];
modernizr -> ready [label="Yes"];
modernizr -> incompatible [label="No"];
incompatible -> ready [label="user ignore"];
olm -> ready [color=red];
config -> ready [color=red];
skin -> ready [color=red];
theme -> ready [color=red];
i18n -> ready [color=red];
}
subgraph cluster_1 {
color = green;
node [style=filled];
label = "init.tsx";
ready -> loadApp;
loadApp -> matrixchat;
}
}
</code></pre>
</p>
</details>
```
Key:
- Parallelogram: async/await task
- Box: sync task
- Diamond: conditional branch
- Egg: user interaction
- Circle: user interaction
- Blue arrow: async task is allowed to settle but allowed to fail
- Red arrow: async task success is asserted
@ -86,4 +75,34 @@ Notes:
- Everything is awaited to be settled before the Modernizr check, to allow it to make use of things like i18n if they are successful.
Underlying dependencies:
![image](https://user-images.githubusercontent.com/2403652/73848977-08624500-4821-11ea-9830-bb0317c41086.png)
```mermaid
flowchart TD
A((rageshake))
B{mobile}
C((config))
D(((olm)))
E((i18n))
F(((load_modernizr)))
G(((modernizr)))
H(((skin)))
I(((theme)))
X[app]
A --> G
A --> B
A-- assert -->X
F --> G --> X
G --> H --> X
C --> I --> X
C --> E --> X
E --> G
B --> C-- assert -->X
B --> D --> X
style X stroke:red
style G stroke:red
style E stroke:lime
linkStyle 0,11 stroke:yellow;
linkStyle 2,13 stroke:red;
```

View File

@ -54,8 +54,8 @@ One of the following options **must** be supplied:
being optional.
If both `default_server_config` and `default_server_name` are used, Element will try to look up the connection
infomation using `.well-known`, and if that fails, take `default_server_config` as the homeserver connection
infomation.
information using `.well-known`, and if that fails, take `default_server_config` as the homeserver connection
information.
## Labs flags
@ -139,7 +139,7 @@ complete re-branding/private labeling, a more personalised experience can be ach
configuration found in the well-known location is used instead.
10. `welcome_user_id`: An optional user ID to start a DM with after creating an account. Defaults to nothing (no DM created).
11. `custom_translations_url`: An optional URL to allow overriding of translatable strings. The JSON file must be in a format of
`{"affected string": {"languageCode": "new string"}}`. See https://github.com/matrix-org/matrix-react-sdk/pull/7886 for details.
`{"affected|translation|key": {"languageCode": "new string"}}`. See https://github.com/matrix-org/matrix-react-sdk/pull/7886 for details.
12. `branding`: Options for configuring various assets used within the app. Described in more detail down below.
13. `embedded_pages`: Further optional URLs for various assets used within the app. Described in more detail down below.
14. `disable_3pid_login`: When `false` (default), **enables** the options to log in with email address or phone number. Set to
@ -355,6 +355,8 @@ If you run your own rageshake server to collect bug reports, the following optio
2. `uisi_autorageshake_app`: If a user has enabled the "automatically send debug logs on decryption errors" flag, this option will be sent
alongside the rageshake so the rageshake server can filter them by app name. By default, this will be `element-auto-uisi`
(in contrast to other rageshakes submitted by the app, which use `element-web`).
3. `existing_issues_url`: URL for where to find existing issues.
4. `new_issue_url`: URL for where to submit new issues.
If you would like to use [Sentry](https://sentry.io/) for rageshake data, add a `sentry` object to your config with the following values:

View File

@ -36,7 +36,7 @@ When `force_disable` is true:
- any `io.element.e2ee.default` value will be disregarded.
Note: If the server is configured to forcibly enable encryption for some or all rooms,
this behaviour will be overriden.
this behaviour will be overridden.
# Secure backup

78
docs/install.md Normal file
View File

@ -0,0 +1,78 @@
# Installing Element Web
**Familiarise yourself with the [Important Security Notes](../README.md#important-security-notes) before starting, they apply to all installation methods.**
_Note: that for the security of your chats will need to serve Element over HTTPS.
Major browsers also do not allow you to use VoIP/video chats over HTTP, as WebRTC is only usable over HTTPS.
There are some exceptions like when using localhost, which is considered a [secure context](https://developer.mozilla.org/docs/Web/Security/Secure_Contexts) and thus allowed._
## Release tarball
1. Download the latest version from <https://github.com/vector-im/element-web/releases>
1. Untar the tarball on your web server
1. Move (or symlink) the `element-x.x.x` directory to an appropriate name
1. Configure the correct caching headers in your webserver (see below)
1. Configure the app by copying `config.sample.json` to `config.json` and
modifying it. See the [configuration docs](docs/config.md) for details.
1. Enter the URL into your browser and log into Element!
Releases are signed using gpg and the OpenPGP standard,
and can be checked against the public key located at <https://packages.element.io/element-release-key.asc>.
## Debian package
Element Web is now also available as a Debian package for Debian and Ubuntu based systems.
```shell
sudo apt install -y wget apt-transport-https
sudo wget -O /usr/share/keyrings/element-io-archive-keyring.gpg https://packages.element.io/debian/element-io-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/element-io-archive-keyring.gpg] https://packages.element.io/debian/ default main" | sudo tee /etc/apt/sources.list.d/element-io.list
sudo apt update
sudo apt install element-web
```
Configure the app by modifying `/etc/element-web/config.json`. See the [configuration docs](docs/config.md) for details.
## Docker
The Docker image can be used to serve element-web as a web server. The easiest way to use
it is to use the prebuilt image:
```bash
docker run -p 80:80 vectorim/element-web
```
To supply your own custom `config.json`, map a volume to `/app/config.json`. For example,
if your custom config was located at `/etc/element-web/config.json` then your Docker command
would be:
```bash
docker run -p 80:80 -v /etc/element-web/config.json:/app/config.json vectorim/element-web
```
To build the image yourself:
```bash
git clone https://github.com/vector-im/element-web.git element-web
cd element-web
git checkout master
docker build .
```
If you're building a custom branch, or want to use the develop branch, check out the appropriate
element-web branch and then run:
```bash
docker build -t \
--build-arg USE_CUSTOM_SDKS=true \
--build-arg REACT_SDK_REPO="https://github.com/matrix-org/matrix-react-sdk.git" \
--build-arg REACT_SDK_BRANCH="develop" \
--build-arg JS_SDK_REPO="https://github.com/matrix-org/matrix-js-sdk.git" \
--build-arg JS_SDK_BRANCH="develop" \
.
```
## Kubernetes
The provided element-web docker image can also be run from within a Kubernetes cluster.
See the [Kubernetes example](docs/kubernetes.md) for more details.

View File

@ -77,15 +77,6 @@ For some sample themes, check out [aaronraimist/element-themes](https://github.c
Allows users to receive encrypted messages by creating a device that is stored
encrypted on the server, as described in [MSC2697](https://github.com/matrix-org/matrix-doc/pull/2697).
## Right panel stays open (`feature_right_panel_default_open`)
This is an experimental default open right panel mode as a quick fix for those
who prefer to have the right panel open consistently across rooms.
If no right panel state is known for the room or it was closed on the last room
visit, it will default to the room member list. Otherwise, the saved card last
used in that room is shown.
## Live location sharing (`feature_location_share_live`) [In Development]
Enables sharing your current location to the timeline, with live updates.
@ -106,17 +97,33 @@ This feature allows users to place and join native [MSC3401](https://github.com/
If you're enabling this at the deployment level, you may also want to reference the docs for the `element_call` config section.
## Disable per-sender encryption for Element Call (`feature_disable_call_per_sender_encryption`)
The default for embedded Element Call in Element Web is per-participant encryption.
This labs flag disables encryption for embedded Element Call in encrypted rooms.
Under the hood this stops Element Web from adding the `perParticipantE2EE` flag for the Element Call widget url.
This is useful while we experiment with encryption and to make calling compatible with platforms that don't use encryption yet.
## Rich text in room topics (`feature_html_topic`) [In Development]
Enables rendering of MD / HTML in room topics.
## Exploring public spaces (`feature_exploring_public_spaces`)
Enables exploring public spaces in the new search dialog. Requires the server to
have [MSC3827](https://github.com/matrix-org/matrix-spec-proposals/pull/3827) enabled.
## Use the Rust cryptography implementation (`feature_rust_crypto`) [In Development]
Configures Element to use a new cryptography implementation based on the [matrix-rust-sdk](https://github.com/matrix-org/matrix-rust-sdk).
This setting is (currently) _sticky_ to a user's session: it only takes effect when the user logs in to a new session. Likewise, even after disabling the setting in `config.json`, the Rust implemention will remain in use until users log out.
This setting is (currently) _sticky_ to a user's session: it only takes effect when the user logs in to a new session. Likewise, even after disabling the setting in `config.json`, the Rust implementation will remain in use until users log out.
## New room header & details (`feature_new_room_decoration_ui`) [In Development]
Refactors visually the room header and room sidebar
## Enable the notifications panel in the room header (`feature_notifications`)
Unreliable in encrypted rooms.
## Knock rooms (`feature_ask_to_join`) [In Development]
Enables knock feature for rooms. This allows users to ask to join a room.

14
docs/lib/custom.css Normal file
View File

@ -0,0 +1,14 @@
/* Prevent collapsible headings from wrapping onto two lines eagerly */
summary > h1,
summary > h2,
summary > h3,
summary > h4,
summary > h5,
summary > h6 {
display: inline-block;
}
/* Prevent longer checkbox lists from wrapping eagerly */
input + p {
display: inline;
}

1
docs/lib/mermaid-init.js Normal file
View File

@ -0,0 +1 @@
mermaid.initialize({ startOnLoad:true });

1648
docs/lib/mermaid.min.js vendored Normal file

File diff suppressed because one or more lines are too long

43
docs/oidc.md Normal file
View File

@ -0,0 +1,43 @@
# OIDC and delegated authentication
## Compatibility/OIDC-aware mode
[MSC2965: OIDC provider discovery](https://github.com/matrix-org/matrix-spec-proposals/pull/2965)
[MSC3824: OIDC aware clients](https://github.com/matrix-org/matrix-spec-proposals/pull/3824)
This mode uses an SSO flow to gain a `loginToken` from the authentication provider, then continues with SSO login.
Element Web uses [MSC2965: OIDC provider discovery](https://github.com/matrix-org/matrix-spec-proposals/pull/2965) to discover the configured provider.
Wherever valid MSC2965 configuration is discovered, OIDC-aware login flow will be the only option offered.
## (🧪Experimental) OIDC-native flow
Can be enabled by a config-level-only setting in `config.json`
```json
{
"features": {
"feature_oidc_native_flow": true
}
}
```
See https://areweoidcyet.com/client-implementation-guide/ for implementation details.
Element Web uses [MSC2965: OIDC provider discovery](https://github.com/matrix-org/matrix-spec-proposals/pull/2965) to discover the configured provider.
Where OIDC native login flow is enabled and valid MSC2965 configuration is discovered, OIDC native login flow will be the only login option offered.
Element Web will attempt to [dynamically register](https://openid.net/specs/openid-connect-registration-1_0.html) with the configured OP.
Then, authentication will be completed [as described here](https://areweoidcyet.com/client-implementation-guide/).
#### Statically configured OIDC clients
Clients that are already registered with the OP can configure their `client_id` in `config.json`.
Where static configuration exists for the OP dynamic client registration will not be attempted.
```json
{
"oidc_static_clients": {
"https://dummyoidcprovider.com/": {
"client_id": "abc123"
}
}
}
```

268
docs/release.md Normal file
View File

@ -0,0 +1,268 @@
> Tip: Paste this into the browser console to make the checkboxes on this page tickable. (Bear in mind that your ticks will be lost if you reload though.)
>
> ```
> document.querySelectorAll("input[type='checkbox']").forEach(i => {i.disabled = false;})
> ```
<details><summary><h1>Branches</h1></summary><blockquote>
#### develop
The develop branch holds the very latest and greatest code we have to offer, as such it may be less stable. It corresponds to the develop.element.io CD platform.
#### staging
The staging branch corresponds to the very latest release regardless of whether it is an RC or not. Deployed to staging.element.io manually.
#### master
The master branch is the most stable as it is the very latest non-RC release. Deployed to app.element.io manually.
</blockquote></details>
<details><summary><h1>Versions</h1></summary><blockquote>
The matrix-js-sdk follows semver, the matrix-react-sdk loosely follows semver, most releases for both will bump the minor version number.
Breaking changes will bump the major version number.
Element Web & Element Desktop do not follow semver and always have matching version numbers. The patch version number is normally incremented for every release.
</blockquote></details>
<details><summary><h1>Release Types</h1></summary><blockquote>
#### Release candidate
A normal release begins with a Release Candidate on the Tick phase of the release cycle,
and may contain as many further RCs as are needed before the Tock phase of cycle.
Each subsequent RC may add additional commits via any of the means of preparation.
A normal release is the most typical run-of-the-mill release,
with at least one RC (Release Candidate) followed by a FINAL release.
The typical cadence for these is every 2 weeks we'll do a new initial RC,
then the following week we'll do that release cycle's FINAL release with sometimes more RCs in between, as needed.
#### Final
A normal release culminates with a Final release on the Tock phase of the cycle.
This may be merely shipping the very latest RC with an adjusted version number,
but can also include (hopefully small) additional changes present on `staging` if they are deemed safe to skip an RC.
### Hotfix / Security
This is an accelerated type of release which sits somewhere between RC and Final.
They tend to contain few patches delta from the previous release but also skip any form of RC
and in the case of Security the patch lands on GitHub only moments prior.
For all intents and purposes they are the same as a Final release but with a different purpose.
</blockquote></details>
<details><summary><h1>Release Blockers</h1></summary><blockquote>
You should become release rabbit on the day after the last full release.
For that week, it's your job to keep an eye on the Releases room and see whether any issues marked `X-Release-Blocker` are opened,
or were already open. You should chase people to fix them, so that on RC day you can make the release.
If release-blocking issues are still open, you need to delay the release until they are fixed or reclassified.
There are two labels for tracking release blockers.
#### X-Release-Blocker
This label applied to an issue means we cannot ship a release affected by the specific issue.
This means we cannot cut branches for an RC but security & hotfix releases may still be fine.
#### X-Upcoming-Release-Blocker
This label applied to an issue means that the next (read: not current) release cycle will be affected by the specific issue.
This label will automagically convert to `X-Release-Blocker` at the conclusion of a full release.
</blockquote></details>
<details><summary><h1>Repositories</h1></summary><blockquote>
This release process revolves around our four main repositories:
- [Element Desktop](https://github.com/vector-im/element-desktop/)
- [Element Web](https://github.com/vector-im/element-web/)
- [Matrix React SDK](https://github.com/matrix-org/matrix-react-sdk/)
- [Matrix JS SDK](https://github.com/matrix-org/matrix-js-sdk/)
We own other repositories, but they have more ad-hoc releases and are not part of the bi-weekly cycle:
- https://github.com/matrix-org/matrix-web-i18n/
- https://github.com/matrix-org/matrix-react-sdk-module-api
</blockquote></details>
<details><summary><h1>Prerequisites</h1></summary><blockquote>
- You must be part of the 2 Releasers GitHub groups:
- <https://github.com/orgs/vector-im/teams/element-web-releasers>
- <https://github.com/orgs/matrix-org/teams/element-web-releasers>
- You will need access to the **VPN** ([docs](https://gitlab.matrix.org/new-vector/internal/-/wikis/SRE/Tailscale)) to be able to follow the instructions under Deploy below.
- You will need the ability to **SSH** in to the production machines to be able to follow the instructions under Deploy below. Ensure that your SSH key has a non-empty passphrase, and you registered your SSH key with Ops. Log a ticket at https://github.com/matrix-org/matrix-ansible-private and ask for:
- Two-factor authentication to be set up on your SSH key. (This is needed to get access to production).
- SSH access to `horme` (staging.element.io and app.element.io)
- Permission to sudo on horme as the user `element`
- You need "**jumphost**" configuration in your local `~/.ssh/config`. This should have been set up as part of your onboarding.
</blockquote></details>
<details><summary><h1>Overview</h1></summary><blockquote>
```mermaid
flowchart TD
P[[Prepare staging branches]]
P --> R1
subgraph Releasing
R1[[Releasing matrix-js-sdk]]
R2[[Releasing matrix-react-sdk]]
R3[[Releasing element-web]]
R4[[Releasing element-desktop]]
R1 --> R2 --> R3 --> R4
end
R4 --> D1
subgraph Deploying
D1[\Deploy staging.element.io/]
D2[\Check dockerhub/]
D3[\Deploy app.element.io/]
D4[\Check desktop package/]
D1 --> D2 --> D
D{FINAL?}
D -->|Yes| D3 --> D4
end
D -->|No| H1
D4 --> H1
subgraph Housekeeping
H1[\Update topics/]
H2[\Announce/]
H3[\Archive done column/]
H4[\Add diary entry/]
H5[\Renovate/]
H1 --> H2 --> H
H{FINAL?}
H -->|Yes| H3 --> H4 --> DONE
H -->|No| H5
end
DONE([You are done!])
H5 --> DONE
```
</blockquote></details>
---
# Preparation
The goal of this stage is to get the code you want to ship onto the `staging` branch.
There are multiple ways to accomplish this depending on the type of release you need to perform.
For the first RC in a given release cycle the easiest way to prepare branches is using the
[Cut branches automation](https://github.com/vector-im/element-web/actions/workflows/release_prepare.yml) -
this will take `develop` and merge it into the `staging` on the chosen repositories.
For subsequent RCs, if you need to include a change you may PR it directly to the `staging` branch or rely on the
backport automation via labelling a PR to `develop` with `backport staging` which will cause a new PR to be opened
which backports the requested change to the `staging` branch.
For security, you may wish to merge the security advisory private fork or apply the patches manually and then push them directly to `staging`.
It is worth noting that at the end of the Final/Hotfix/Security release `staging` is merged to `master` which is merged back into `develop` -
this means that any commit which goes to `staging` will eventually make its way back to the default branch.
- [ ] The staging branch is prepared
# Releasing
Shortly after concluding the preparation stage (or pushing any changes to `staging` in general);
a draft release will be automatically made on the 4 project repositories with suggested changelogs and version numbers.
Review the draft releases created, check the version number makes sense and that the changelog contains everything you'd expect to.
_Note: we should add a step here to write summaries atop the changelogs manually, or via AI_
### Matrix JS SDK
The first stop is the matrix-js-sdk; kick off a release using [the automation](https://github.com/matrix-org/matrix-js-sdk/actions/workflows/release.yml) - making sure to select the right type of release. For anything other than an RC: choose final. You should not need to ever switch off either of the Publishing options.
- [ ] matrix-js-sdk has been released & published to npm
### Matrix React SDK
The next stop is matrix-react-sdk; kick off a release using [the automation](https://github.com/matrix-org/matrix-react-sdk/actions/workflows/release.yml) - making sure to select the right type of release. For anything other than an RC: choose final. In the JS SDK version field enter the version of the JS SDK you wish to use, for typical releases including all the layers this would be the version released in the stage above.
- [ ] matrix-react-sdk has been released & published to npm
### Element Web
The next stop is element-web; kick off a release using [the automation](https://github.com/vector-im/element-web/actions/workflows/release.yml) - making sure to select the right type of release. For anything other than an RC: choose final. In the SDK version fields enter the versions you wish to use, for typical releases including all the layers this would be the versions released in the stages above.
- [ ] Element Web has been released
### Element Desktop
The next stop is element-desktop; kick off a release using [the automation](https://github.com/vector-im/element-desktop/actions/workflows/release.yml) - making sure to select the right type of release. For anything other than an RC: choose final. In the JS SDK version field enter the version of the JS SDK you wish to use, for typical releases including all the layers this would be the version released in the stage above.
- [ ] Element Desktop has been released
# Deploying
We ship the SDKs to npm, this happens as part of the release process.
We ship Element Web to dockerhub, `*.element.io`, and packages.element.io.
We ship Element Desktop to packages.element.io.
- [ ] Check that element-web has shipped to dockerhub
- [ ] Deploy staging.element.io. [See docs.](https://handbook.element.io/books/element-web-team/page/deploying-appstagingelementio)
- [ ] Test staging.element.io
For final releases additionally do these steps:
- [ ] Deploy app.element.io. [See docs.](https://handbook.element.io/books/element-web-team/page/deploying-appstagingelementio)
- [ ] Test app.element.io
- [ ] Ensure Element Web package has shipped to packages.element.io
- [ ] Ensure Element Desktop packages have shipped to packages.element.io
# Housekeeping
We have some manual housekeeping to do in order to prepare for the next release.
- [ ] Update topics using [the automation](https://github.com/vector-im/element-web/actions/workflows/update-topics.yaml). It will autodetect the current latest version. Don't forget the date you supply should be e.g. September 5th (including the "th") for the script to work.
- [ ] Announce the release in [#element-web-announcements:matrix.org](https://matrix.to/#/#element-web-announcements:matrix.org)
<details><summary>(show)</summary>
With wording like:
> Element Web v1.11.24 is here!
>
> This version adds ... and fixes bugs ...
>
> Check it out at app.element.io, in Element Desktop, or from Docker Hub. Changelog and more details at https://github.com/vector-im/element-web/releases/tag/v1.11.24
</details>
For the first RC of a given release cycle do these steps:
- [ ] Go to the [matrix-js-sdk Renovate dashboard](https://github.com/matrix-org/matrix-js-sdk/issues/2406) and click the checkbox to create/update its PRs.
- [ ] Go to the [matrix-react-sdk Renovate dashboard](https://github.com/matrix-org/matrix-react-sdk/issues/9667) and click the checkbox to create/update its PRs.
- [ ] Go to the [element-web Renovate dashboard](https://github.com/vector-im/element-web/issues/22941) and click the checkbox to create/update its PRs.
- [ ] Go to the [element-desktop Renovate dashboard](https://github.com/vector-im/element-desktop/issues/465) and click the checkbox to create/update its PRs.
- [ ] Later, check back and merge the PRs that succeeded to build. The ones that failed will get picked up by the [maintainer](https://docs.google.com/document/d/1V5VINWXATMpz9UBw4IKmVVB8aw3CxM0Jt7igtHnDfSk/edit#).
For final releases additionally do these steps:
- [ ] Archive done column on the [team board](https://github.com/orgs/vector-im/projects/67/views/34) _Note: this should be automated_
- [ ] Add entry to the [milestones diary](https://docs.google.com/document/d/1cpRFJdfNCo2Ps6jqzQmatzbYEToSrQpyBug0aP_iwZE/edit#heading=h.6y55fw4t283z). The document says only to add significant releases, but we add all of them just in case.

View File

@ -6,11 +6,16 @@
- Including up-to-date versions of matrix-react-sdk and matrix-js-sdk
- Latest LTS version of Node.js installed
- Be able to understand English
- Be able to understand the language you want to translate Element into
## Translating strings vs. marking strings for translation
Translating strings are done with the `_t()` function found in matrix-react-sdk/lib/languageHandler.js. It is recommended to call this function wherever you introduce a string constant which should be translated. However, translating can not be performed until after the translation system has been initialized. Thus, sometimes translation must be performed at a different location in the source code than where the string is introduced. This breaks some tooling and makes it difficult to find translatable strings. Therefore, there is the alternative `_td()` function which is used to mark strings for translation, without actually performing the translation (which must still be performed separately, and after the translation system has been initialized).
Translating strings are done with the `_t()` function found in matrix-react-sdk/lib/languageHandler.js.
It is recommended to call this function wherever you introduce a string constant which should be translated.
However, translating can not be performed until after the translation system has been initialized.
Thus, sometimes translation must be performed at a different location in the source code than where the string is introduced.
This breaks some tooling and makes it difficult to find translatable strings.
Therefore, there is the alternative `_td()` function which is used to mark strings for translation,
without actually performing the translation (which must still be performed separately, and after the translation system has been initialized).
Basically, whenever a translatable string is introduced, you should call either `_t()` immediately OR `_td()` and later `_t()`.
@ -29,27 +34,39 @@ function getColorName(hex) {
}
```
## Key naming rules
These rules are based on https://github.com/vector-im/element-x-android/blob/develop/tools/localazy/README.md
At this time we are not trying to have a translation key per UI element as some methodologies use,
whilst that would offer the greatest flexibility, it would also make reuse between projects nigh impossible.
We are aiming for a set of common strings to be shared then some more localised translations per context they may appear in.
1. Ensure the string doesn't already exist in a related project, such as https://localazy.com/p/element
2. Keys for common strings, i.e. strings that can be used at multiple places must start by `action_` if this is a verb, or `common_` if not
3. Keys for common accessibility strings must start by `a11y_`. Example: `a11y_hide_password`
4. Otherwise, try to group keys logically and nest where appropriate, such as `keyboard_` for strings relating to keyboard shortcuts.
5. Ensure your translation keys do not include `.` or `|` or ` `. Try to balance string length against descriptiveness.
## Adding new strings
1. Check if the import `import { _t } from 'matrix-react-sdk/src/languageHandler';` is present. If not add it to the other import statements. Also import `_td` if needed.
1. Add `_t()` to your string. (Don't forget curly braces when you assign an expression to JSX attributes in the render method). If the string is introduced at a point before the translation system has not yet been initialized, use `_td()` instead, and call `_t()` at the appropriate time.
1. Run `yarn i18n` to update `src/i18n/strings/en_EN.json`
1. If you added a string with a plural, you can add other English plural variants to `src/i18n/strings/en_EN.json` (remeber to edit the one in the same project as the source file containing your new translation).
1. Check if the import `import { _t } from 'matrix-react-sdk/src/languageHandler';` is present. If not add it to the other import statements. Also import `_td` if needed.
1. Add `_t()` to your string passing the translation key you come up with based on the rules above. If the string is introduced at a point before the translation system has not yet been initialized, use `_td()` instead, and call `_t()` at the appropriate time.
1. Run `yarn i18n` to add the keys to `src/i18n/strings/en_EN.json`
1. Modify the new entries in `src/i18n/strings/en_EN.json` with the English (UK) translations for the added keys.
## Editing existing strings
1. Edit every occurrence of the string inside `_t()` and `_td()` in the JSX files.
1. Run `yarn i18n` to update `src/i18n/strings/en_EN.json`. (Be sure to run this in the same project as the JSX files you just edited.)
1. Run `yarn prunei18n` to remove the old string from `src/i18n/strings/*.json`.
Edits to existing strings should be performed only via Localazy.
There you can also require all translations to be redone if the meaning of the string has changed significantly.
## Adding variables inside a string.
1. Extend your `_t()` call. Instead of `_t(STRING)` use `_t(STRING, {})`
1. Extend your `_t()` call. Instead of `_t(TKEY)` use `_t(TKEY, {})`
1. Decide how to name it. Please think about if the person who has to translate it can understand what it does. E.g. using the name 'recipient' is bad, because a translator does not know if it is the name of a person, an email address, a user ID, etc. Rather use e.g. recipientEmailAddress.
1. Add it to the array in `_t` for example `_t(STRING, {variable: this.variable})`
1. Add it to the array in `_t` for example `_t(TKEY, {variable: this.variable})`
1. Add the variable inside the string. The syntax for variables is `%(variable)s`. Please note the _s_ at the end. The name of the variable has to match the previous used name.
- You can use the special `count` variable to choose between multiple versions of the same string, in order to get the correct pluralization. E.g. `_t('You have %(count)s new messages', { count: 2 })` would show 'You have 2 new messages', while `_t('You have %(count)s new messages', { count: 1 })` would show 'You have one new message' (assuming a singular version of the string has been added to the translation file. See above). Passing in `count` is much prefered over having an if-statement choose the correct string to use, because some languages have much more complicated plural rules than english (e.g. they might need a completely different form if there are three things rather than two).
- You can use the special `count` variable to choose between multiple versions of the same string, in order to get the correct pluralization. E.g. `_t('You have %(count)s new messages', { count: 2 })` would show 'You have 2 new messages', while `_t('You have %(count)s new messages', { count: 1 })` would show 'You have one new message' (assuming a singular version of the string has been added to the translation file. See above). Passing in `count` is much preferred over having an if-statement choose the correct string to use, because some languages have much more complicated plural rules than english (e.g. they might need a completely different form if there are three things rather than two).
- If you want to translate text that includes e.g. hyperlinks or other HTML you have to also use tag substitution, e.g. `_t('<a>Click here!</a>', {}, { 'a': (sub) => <a>{sub}</a> })`. If you don't do the tag substitution you will end up showing literally '<a>' rather than making a hyperlink.
- You can also use React components with normal variable substitution if you want to insert HTML markup, e.g. `_t('Your email address is %(emailAddress)s', { emailAddress: <i>{userEmailAddress}</i> })`.
@ -61,4 +78,5 @@ function getColorName(hex) {
- Avoid "translation in parts", i.e. concatenating translated strings or using translated strings in variable substitutions. Context is important for translations, and translating partial strings this way is simply not always possible.
- Concatenating strings often also introduces an implicit assumption about word order (e.g. that the subject of the sentence comes first), which is incorrect for many languages.
- Translation 'smell test': If you have a string that does not begin with a capital letter (is not the start of a sentence) or it ends with e.g. ':' or a preposition (e.g. 'to') you should recheck that you are not trying to translate a partial sentence.
- If you have multiple strings, that are almost identical, except some part (e.g. a word or two) it is still better to translate the full sentence multiple times. It may seem like inefficient repetion, but unlike programming where you try to minimize repetition, translation is much faster if you have many, full, clear, sentences to work with, rather than fewer, but incomplete sentence fragments.
- If you have multiple strings, that are almost identical, except some part (e.g. a word or two) it is still better to translate the full sentence multiple times. It may seem like inefficient repetition, but unlike programming where you try to minimize repetition, translation is much faster if you have many, full, clear, sentences to work with, rather than fewer, but incomplete sentence fragments.
- Don't forget curly braces when you assign an expression to JSX attributes in the render method)

View File

@ -6,58 +6,31 @@
- Be able to understand English
- Be able to understand the language you want to translate Element into
## Step 0: Join #element-translations:matrix.org
## Join #element-translations:matrix.org
1. Come and join https://matrix.to/#/#element-translations:matrix.org for general discussion
2. Join https://matrix.to/#/#element-translators:matrix.org for language-specific rooms
3. Read scrollback and/or ask if anyone else is working on your language, and co-ordinate if needed. In general little-or-no coordination is needed though :)
## Step 1: Preparing your Weblate Profile
1. Head to https://translate.element.io and register either via Github or email
2. After registering check if you got an email to verify your account and click the link (if there is none head to step 1.4)
3. Log into weblate
4. Head to https://translate.element.io/accounts/profile/ and select the languages you know and maybe another language you know too.
## How to check if your language already is being translated
Go to https://translate.element.io/projects/element-web/ and visit the 2 sub-projects.
If your language is listed go to Step 2a and if not go to Step 2b
Go to https://localazy.com/p/element-web
If your language is listed then you can get started, have a read of https://localazy.com/docs/general/translating-strings
if you need help getting started. If your language is not yet listed please express your wishes to start translating it in
the general discussion room linked above.
## Step 2a: Helping on existing languages.
### What are `%(something)s`?
1. Head to one of the projects listed https://translate.element.io/projects/element-web/
2. Click on the `translate` button on the right side of your language
3. Fill in the translations in the writeable field. You will see the original English string and the string of your second language above.
These things are placeholders that are expanded when displayed by Element. They can be room names, usernames or similar.
If you find one, you can move to the right place for your language, but not delete it as the variable will be missing if you do.
A special case is `%(count)s` as this is also used to determine which pluralisation is used.
Head to the explanations under Steb 2b
### What are `<link>Something</link>`
## Step 2b: Adding a new language
These things are markup tags, they encapsulate sections of translations to be marked up, with links, buttons, emphasis and such.
You must keep these markers surrounding the equivalent string in your language that needs to be marked up.
1. Go to one of the projects listed https://translate.element.io/projects/element-web/
2. Click the `Start new translation` button at the bottom
3. Select a language
4. Start translating like in 2a.3
5. Repeat these steps for the other projects which are listed at the link of step 2b.1
### When will my translations be available?
### What means the green button under the text field?
The green button let you save our translations directly. Please only use it if you are 100% sure about that translation. If you do not know a translation please DO NOT click that button. Use the arrows above the translations field and click to the right.
### What means the yellow button under the text field?
The yellow button has to be used if you are unsure about the translation but you have a rough idea. It adds a new suggestion to the string which can than be reviewed by others.
### What are "%(something)s"?
These things are variables that are expanded when displayed by Element. They can be room names, usernames or similar. If you find one, you can move to the right place for your language, but not delete it as the variable will be missing if you do.
A special case is `%(urlStart)s` and `%(urlEnd)s` which are used to mark the beginning of a hyperlink (i.e. `<a href="/somewhere">` and `</a>`. You must keep these markers surrounding the equivalent string in your language that needs to be hyperlinked.
### "I want to come back to this string. How?"
You can use inside the translation field "Review needed" checkbox. It will be shown as Strings that need to be reviewed.
### Further reading
The official Weblate doc provides some more in-depth explanation on how to do translations and talks about do and don'ts. You can find it at: https://docs.weblate.org/en/latest/user/translating.html
We automatically pull changes from Localazy 3 times a week, so your translations should be available at https://develop.element.io
within a few days of you submitting them and them being approved. They will then also be included in the following release cycle.

View File

@ -48,10 +48,11 @@
},
"privacy_policy_url": "https://element.io/cookie-policy",
"features": {
"feature_video_rooms": true
"feature_video_rooms": true,
"feature_rust_crypto": true
},
"element_call": {
"url": "https://element-call-livekit.netlify.app"
"url": "https://call.element.dev"
},
"map_style_url": "https://api.maptiler.com/maps/streets/style.json?key=fU3vlMsMn4Jb6dnEIFsx"
}

View File

@ -25,7 +25,7 @@ const config: Config = {
},
testMatch: ["<rootDir>/test/**/*-test.[tj]s?(x)"],
setupFiles: ["jest-canvas-mock"],
setupFilesAfterEnv: ["<rootDir>/node_modules/matrix-react-sdk/test/setupTests.js"],
setupFilesAfterEnv: ["<rootDir>/node_modules/matrix-react-sdk/test/setupTests.ts"],
moduleNameMapper: {
"\\.(css|scss|pcss)$": "<rootDir>/__mocks__/cssMock.js",
"\\.(gif|png|ttf|woff2)$": "<rootDir>/node_modules/matrix-react-sdk/__mocks__/imageMock.js",
@ -40,9 +40,10 @@ const config: Config = {
"waveWorker\\.min\\.js": "<rootDir>/node_modules/matrix-react-sdk/__mocks__/empty.js",
"context-filter-polyfill": "<rootDir>/node_modules/matrix-react-sdk/__mocks__/empty.js",
"FontManager.ts": "<rootDir>/node_modules/matrix-react-sdk/__mocks__/FontManager.js",
"workers/(.+)\\.worker\\.ts": "<rootDir>/node_modules/matrix-react-sdk/__mocks__/workerMock.js",
"workers/(.+)Factory": "<rootDir>/node_modules/matrix-react-sdk/__mocks__/workerFactoryMock.js",
"^!!raw-loader!.*": "jest-raw-loader",
"RecorderWorklet": "<rootDir>/node_modules/matrix-react-sdk/__mocks__/empty.js",
"recorderWorkletFactory": "<rootDir>/node_modules/matrix-react-sdk/__mocks__/empty.js",
"^fetch-mock$": "<rootDir>/node_modules/fetch-mock",
},
transformIgnorePatterns: ["/node_modules/(?!matrix-js-sdk).+$", "/node_modules/(?!matrix-react-sdk).+$"],
coverageReporters: ["text-summary", "lcov"],

37
localazy.json Normal file
View File

@ -0,0 +1,37 @@
{
"readKey": "a7688614897667993891-866e2615b0a22e6ccef56aea9b10e815efa3e1296752a7a30bd9925f1a8f33e7",
"upload": {
"type": "json",
"keySeparator": "|",
"deprecate": "file",
"features": ["plural_object", "filter_untranslated"],
"files": [
{
"pattern": "src/i18n/strings/en_EN.json",
"file": "element-web.json",
"lang": "inherited"
},
{
"group": "existing",
"pattern": "src/i18n/strings/*.json",
"file": "element-web.json",
"excludes": ["src/i18n/strings/en_EN.json"],
"lang": "${autodetectLang}"
}
]
},
"download": {
"files": [
{
"conditions": "equals: ${file}, element-web.json",
"output": "src/i18n/strings/${langLsrUnderscore}.json"
}
],
"includeSourceLang": "${includeSourceLang|false}",
"langAliases": {
"en": "en-EN"
}
}
}

View File

@ -185,9 +185,20 @@ function getModuleApiVersionFor(moduleName: string): string {
return findDepVersionInPackageJson(moduleApiDepName, pkgJsonStr);
}
// A list of Module API versions that are supported in addition to the currently installed one
// defined in the package.json. This is necessary because semantic versioning is applied to both
// the Module-side surface of the API and the Client-side surface of the API. So breaking changes
// in the Client-side surface lead to a major bump even though the Module-side surface stays
// compatible. We aim to not break the Module-side surface so we maintain a list of compatible
// older versions.
const backwardsCompatibleMajorVersions = ["1.0.0"];
function isModuleVersionCompatible(ourApiVersion: string, moduleApiVersion: string): boolean {
if (!moduleApiVersion) return false;
return semver.satisfies(ourApiVersion, moduleApiVersion);
return (
semver.satisfies(ourApiVersion, moduleApiVersion) ||
backwardsCompatibleMajorVersions.some((version) => semver.satisfies(version, moduleApiVersion))
);
}
function writeModulesTs(content: string): void {

View File

@ -1,6 +1,6 @@
{
"name": "element-web",
"version": "1.11.36",
"version": "1.11.50",
"description": "A feature-rich client for Matrix.org",
"author": "New Vector Ltd.",
"repository": {
@ -30,24 +30,25 @@
"UserFriendlyError"
],
"scripts": {
"i18n": "matrix-gen-i18n",
"prunei18n": "matrix-prune-i18n",
"diff-i18n": "cp src/i18n/strings/en_EN.json src/i18n/strings/en_EN_orig.json && matrix-gen-i18n && matrix-compare-i18n-files src/i18n/strings/en_EN_orig.json src/i18n/strings/en_EN.json",
"i18n": "matrix-gen-i18n && 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": "prettier --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",
"clean": "rimraf lib webapp",
"build": "yarn clean && yarn build:genfiles && yarn build:bundle",
"build-stats": "yarn clean && yarn build:genfiles && yarn build:bundle-stats",
"build:jitsi": "node scripts/build-jitsi.js",
"build:res": "node scripts/copy-res.js",
"build:jitsi": "ts-node scripts/build-jitsi.ts",
"build:res": "ts-node scripts/copy-res.ts",
"build:genfiles": "yarn build:res && yarn build:jitsi && yarn build:module_system",
"build:modernizr": "modernizr -c .modernizr.json -d src/vector/modernizr.js",
"build:bundle": "webpack --progress --bail --mode production",
"build:bundle-stats": "webpack --progress --bail --mode production --json > webpack-stats.json",
"build:module_system": "tsc --project ./tsconfig.module_system.json && node ./lib/module_system/scripts/install.js",
"build:bundle": "webpack --progress --mode production",
"build:bundle-stats": "webpack --progress --mode production --json > webpack-stats.json",
"build:module_system": "ts-node --project ./tsconfig.module_system.json module_system/scripts/install.ts",
"dist": "scripts/package.sh",
"start": "yarn build:module_system && concurrently --kill-others-on-fail --prefix \"{time} [{name}]\" -n res,element-js \"yarn start:res\" \"yarn start:js\"",
"start": "concurrently --kill-others-on-fail --prefix \"{time} [{name}]\" -n modules,res,jitsi \"yarn build:module_system\" \"yarn build:res\" \"yarn build:jitsi\" && concurrently --kill-others-on-fail --prefix \"{time} [{name}]\" -n res,element-js \"yarn start:res\" \"yarn start:js\"",
"start:https": "concurrently --kill-others-on-fail --prefix \"{time} [{name}]\" -n res,element-js \"yarn start:res\" \"yarn start:js --https\"",
"start:res": "yarn build:jitsi && node scripts/copy-res.js -w",
"start:js": "webpack-dev-server --output-filename=bundles/_dev_/[name].js --output-chunk-filename=bundles/_dev_/[name].js -w --mode development --disable-host-check --hot",
"start:res": "ts-node scripts/copy-res.ts -w",
"start:js": "webpack serve --output-path webapp --output-filename=bundles/_dev_/[name].js --output-chunk-filename=bundles/_dev_/[name].js --mode development",
"lint": "yarn lint:types && yarn lint:js && yarn lint:style",
"lint:js": "yarn lint:js:src && yarn lint:js:module_system",
"lint:js:src": "eslint --max-warnings 0 src test && prettier --check .",
@ -61,19 +62,20 @@
"lint:style": "stylelint \"res/css/**/*.pcss\"",
"test": "jest",
"coverage": "yarn test --coverage",
"analyse:unused-exports": "node ./scripts/analyse_unused_exports.js",
"analyse:unused-exports": "ts-node ./scripts/analyse_unused_exports.ts",
"analyse:webpack-bundles": "webpack-bundle-analyzer webpack-stats.json webapp"
},
"resolutions": {
"@types/react-dom": "17.0.19",
"@types/react": "17.0.58"
"@types/react-dom": "17.0.21",
"@types/react": "17.0.68"
},
"dependencies": {
"@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.14.tgz",
"@matrix-org/react-sdk-module-api": "^1.0.0",
"@matrix-org/olm": "3.2.15",
"@matrix-org/react-sdk-module-api": "^2.2.1",
"gfm.css": "^1.1.2",
"jsrsasign": "^10.5.25",
"katex": "^0.16.0",
"lodash": "^4.17.21",
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop",
"matrix-react-sdk": "github:matrix-org/matrix-react-sdk#develop",
"matrix-widget-api": "^1.3.1",
@ -99,60 +101,60 @@
"@babel/preset-typescript": "^7.12.7",
"@babel/register": "^7.12.10",
"@babel/runtime": "^7.12.5",
"@casualbot/jest-sonar-reporter": "^2.2.5",
"@casualbot/jest-sonar-reporter": "2.2.7",
"@principalstudio/html-webpack-inject-preload": "^1.2.7",
"@sentry/webpack-plugin": "^2.0.0",
"@sentry/webpack-plugin": "^2.7.1",
"@svgr/webpack": "^5.5.0",
"@testing-library/react": "^12.1.5",
"@types/jest": "^29.0.0",
"@types/jitsi-meet": "^2.0.2",
"@types/jsrsasign": "^10.5.4",
"@types/modernizr": "^3.5.3",
"@types/lodash": "^4.14.197",
"@types/node": "^16",
"@types/react": "17.0.58",
"@types/react-dom": "17.0.19",
"@types/node-fetch": "^2.6.4",
"@types/react": "17.0.68",
"@types/react-dom": "17.0.21",
"@types/ua-parser-js": "^0.7.36",
"@typescript-eslint/eslint-plugin": "^5.45.0",
"@typescript-eslint/parser": "^5.45.0",
"allchange": "^1.0.6",
"babel-jest": "^29.0.0",
"babel-loader": "^8.2.2",
"buffer": "^6.0.3",
"chokidar": "^3.5.1",
"concurrently": "^8.0.0",
"cpx": "^1.5.0",
"copy-webpack-plugin": "^11.0.0",
"cronstrue": "^2.41.0",
"css-loader": "^4",
"css-minimizer-webpack-plugin": "^5.0.1",
"dotenv": "^16.0.2",
"eslint": "8.43.0",
"eslint": "8.53.0",
"eslint-config-google": "^0.14.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-deprecate": "^0.7.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-deprecate": "0.8.4",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-matrix-org": "^1.0.0",
"eslint-plugin-react": "^7.28.0",
"eslint-plugin-react-hooks": "^4.3.0",
"eslint-plugin-unicorn": "^47.0.0",
"extract-text-webpack-plugin": "^4.0.0-beta.0",
"fake-indexeddb": "^4.0.0",
"eslint-plugin-unicorn": "^49.0.0",
"fake-indexeddb": "^5.0.0",
"fetch-mock": "9.11.0",
"fetch-mock-jest": "^1.5.1",
"file-loader": "^6.0.0",
"fs-extra": "^11.0.0",
"html-webpack-plugin": "^4.5.2",
"html-webpack-plugin": "^5.5.3",
"jest": "^29.0.0",
"jest-canvas-mock": "2.5.2",
"jest-environment-jsdom": "^29.0.0",
"jest-mock": "^29.0.0",
"jest-raw-loader": "^1.0.1",
"json-loader": "^0.5.7",
"loader-utils": "^3.0.0",
"matrix-mock-request": "^2.5.0",
"matrix-web-i18n": "^1.4.0",
"mini-css-extract-plugin": "^1",
"matrix-web-i18n": "^3.1.3",
"mini-css-extract-plugin": "^2.7.6",
"minimist": "^1.2.6",
"mkdirp": "^3.0.0",
"modernizr": "^3.12.0",
"node-fetch": "^2.6.7",
"optimize-css-assets-webpack-plugin": "^6.0.0",
"postcss": "^8.4.16",
"postcss": "^8.4.31",
"postcss-easings": "^2.0.0",
"postcss-hexrgba": "2.0.1",
"postcss-import": "^12.0.1",
@ -163,26 +165,27 @@
"postcss-scss": "^4.0.4",
"postcss-simple-vars": "^5.0.2",
"prettier": "2.8.8",
"process": "^0.11.10",
"proxy-agent": "^6.3.0",
"raw-loader": "^4.0.2",
"rimraf": "^5.0.0",
"semver": "^7.5.2",
"simple-proxy-agent": "^1.1.0",
"setimmediate": "^1.0.5",
"string-replace-loader": "3",
"style-loader": "2",
"stylelint": "^15.10.1",
"stylelint-config-standard": "^34.0.0",
"stylelint-scss": "^5.0.0",
"terser-webpack-plugin": "^4.0.0",
"terser-webpack-plugin": "^5.3.9",
"ts-node": "^10.9.1",
"ts-prune": "^0.10.3",
"typescript": "5.0.4",
"webpack": "^4.46.0",
"typescript": "5.2.2",
"util": "^0.12.5",
"webpack": "^5.89.0",
"webpack-bundle-analyzer": "^4.8.0",
"webpack-cli": "^3.3.12",
"webpack-dev-server": "^3.11.2",
"worker-loader": "^3.0.0",
"worklet-loader": "^2.0.0",
"yaml": "^2.0.1"
"webpack-cli": "^4.10.0",
"webpack-dev-server": "^4.15.1",
"yaml": "^2.3.3"
},
"@casualbot/jest-sonar-reporter": {
"outputDirectory": "coverage",

View File

@ -0,0 +1,45 @@
/*
Copyright 2023 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Inspired by https://github.com/reklawnos/worklet-loader which doesn't
// formally support Webpack 5
const SingleEntryPlugin = require("webpack/lib/SingleEntryPlugin");
module.exports = function () {};
module.exports.pitch = function pitch(request) {
const cb = this.async();
const filename = "recorder.worklet.js";
const compiler = this._compilation.createChildCompiler("worker", {
filename,
chunkFilename: `[id].${filename}`,
namedChunkFilename: null,
});
new SingleEntryPlugin(this.context, `!!${request}`, "main").apply(compiler);
compiler.runAsChild((err, entries, compilation) => {
if (err) {
return cb(err);
}
if (entries[0]) {
return cb(null, `module.exports = __webpack_public_path__ + ${JSON.stringify(entries[0].files[0])};`);
}
return cb(null, null);
});
};

View File

@ -1,26 +1,35 @@
{
"applinks": {
"apps": [],
"details": [
{
"appIDs":[
"7J4U792NQT.im.vector.app",
"7J4U792NQT.io.element.elementx",
"7J4U792NQT.io.element.elementx.nightly",
"7J4U792NQT.io.element.elementx.pr"
],
"paths": [
"*"
]
}
"applinks": {
"details": [
{
"appIDs": [
"7J4U792NQT.im.vector.app",
"7J4U792NQT.io.element.elementx",
"7J4U792NQT.io.element.elementx.nightly",
"7J4U792NQT.io.element.elementx.pr"
],
"components": [
{
"?": {
"no_universal_links": "?*"
},
"exclude": true,
"comment": "Opt out of universal links"
},
{
"/": "/*",
"comment": "Matches any URL"
}
]
},
"webcredentials": {
"apps": [
"7J4U792NQT.im.vector.app",
"7J4U792NQT.io.element.elementx",
"7J4U792NQT.io.element.elementx.nightly",
"7J4U792NQT.io.element.elementx.pr"
]
}
}
]
},
"webcredentials": {
"apps": [
"7J4U792NQT.im.vector.app",
"7J4U792NQT.io.element.elementx",
"7J4U792NQT.io.element.elementx.nightly",
"7J4U792NQT.io.element.elementx.pr"
]
}
}

View File

@ -77,6 +77,11 @@
"url": "https://play.google.com/store/apps/details?id=im.vector.app",
"id": "im.vector.app"
},
{
"platform": "f-droid",
"url": "https://f-droid.org/repository/browse/?fdid=im.vector.app",
"id": "im.vector.app"
},
{
"platform": "itunes",
"url": "https://apps.apple.com/app/vector/id1083446067"

View File

@ -169,22 +169,22 @@ we don't have an account and should hide them. No account == no guest account ei
<a href="https://element.io" target="_blank" rel="noopener">
<img src="$logoUrl" alt="" class="mx_Logo" />
</a>
<h1 class="mx_Header_title">_t("Welcome to Element")</h1>
<h1 class="mx_Header_title">_t("welcome_to_element")</h1>
<!-- XXX: Our translations system isn't smart enough to recognize variables in the HTML, so we manually do it -->
<h4 class="mx_Header_subtitle">_t("Decentralised, encrypted chat &amp; collaboration powered by $matrixLogo")</h4>
<h4 class="mx_Header_subtitle">_t("powered_by_matrix_with_logo")</h4>
<div class="mx_ButtonGroup">
<div class="mx_ButtonRow">
<a href="#/login" class="mx_ButtonParent mx_ButtonSignIn mx_Button_iconSignIn">
<div class="mx_ButtonLabel">_t("Sign In")</div>
<div class="mx_ButtonLabel">_t("action|sign_in")</div>
</a>
<a href="#/register" class="mx_ButtonParent mx_ButtonCreateAccount mx_Button_iconCreateAccount">
<div class="mx_ButtonLabel">_t("Create Account")</div>
<div class="mx_ButtonLabel">_t("action|create_account")</div>
</a>
</div>
<div class="mx_ButtonRow mx_WelcomePage_guestFunctions">
<div>
<a href="#/directory" class="mx_ButtonParent mx_SecondaryButton mx_Button_iconRoomDirectory">
<div class="mx_ButtonLabel">_t("Explore rooms")</div>
<div class="mx_ButtonLabel">_t("action|explore_rooms")</div>
</a>
</div>
</div>

View File

@ -1,13 +1,12 @@
#!/usr/bin/env node
"use strict";
const fs = require("fs");
const { exec } = require("node:child_process");
import * as fs from "node:fs";
import { exec } from "node:child_process";
const includeJSSDK = process.argv.includes("--include-js-sdk");
const ignore = [];
const ignore: string[] = [];
ignore.push(...Object.values(JSON.parse(fs.readFileSync(`${__dirname}/../components.json`))));
ignore.push(...Object.values<string>(JSON.parse(fs.readFileSync(`${__dirname}/../components.json`, "utf-8"))));
ignore.push("/index.ts");
// We ignore js-sdk by default as it may export for other non element-web projects
if (!includeJSSDK) ignore.push("matrix-js-sdk");
@ -31,7 +30,7 @@ exec(command, (error, stdout, stderr) => {
// won't have an "/" character at the start, so we try to fix that for
// better UX
// TODO: This might break on Windows
lines = lines.reduce((newLines, line) => {
lines = lines.reduce<string[]>((newLines, line) => {
if (!line.startsWith("/")) newLines.push("/" + line);
else newLines.push(line);
return newLines;

View File

@ -3,11 +3,11 @@
// due to file associations in Windows.
// Sorry.
const fs = require("fs");
const path = require("path");
const { mkdirpSync } = require("mkdirp");
const fetch = require("node-fetch");
const ProxyAgent = require("simple-proxy-agent");
import * as fs from "node:fs";
import * as path from "node:path";
import { mkdirpSync } from "mkdirp";
import fetch from "node-fetch";
import { ProxyAgent } from "proxy-agent";
console.log("Making webapp directory");
mkdirpSync("webapp");
@ -16,15 +16,12 @@ mkdirpSync("webapp");
console.log("Downloading Jitsi script");
const fname = path.join("webapp", "jitsi_external_api.min.js");
const options = {};
if (process.env.HTTPS_PROXY) {
options.agent = new ProxyAgent(process.env.HTTPS_PROXY, { tunnel: true });
}
fetch("https://meet.element.io/libs/external_api.min.js", options)
fetch("https://meet.element.io/libs/external_api.min.js", {
agent: new ProxyAgent(),
})
.then((res) => {
const stream = fs.createWriteStream(fname);
return new Promise((resolve, reject) => {
return new Promise<void>((resolve, reject) => {
res.body.pipe(stream);
res.body.on("error", (err) => reject(err));
res.body.on("finish", () => resolve());

View File

@ -1 +0,0 @@
../../matrix-react-sdk/scripts/check-i18n.pl

View File

@ -1,293 +0,0 @@
#!/usr/bin/env node
const loaderUtils = require("loader-utils");
// copies the resources into the webapp directory.
//
// Languages are listed manually so we can choose when to include
// a translation in the app (because having a translation with only
// 3 strings translated is just frustrating)
// This could readily be automated, but it's nice to explicitly
// control when new languages are available.
const INCLUDE_LANGS = [
{ value: "bg", label: "Български" },
{ value: "ca", label: "Català" },
{ value: "cs", label: "čeština" },
{ value: "da", label: "Dansk" },
{ value: "de_DE", label: "Deutsch" },
{ value: "el", label: "Ελληνικά" },
{ value: "en_EN", label: "English" },
{ value: "en_US", label: "English (US)" },
{ value: "eo", label: "Esperanto" },
{ value: "es", label: "Español" },
{ value: "et", label: "Eesti" },
{ value: "eu", label: "Euskara" },
{ value: "fi", label: "Suomi" },
{ value: "fr", label: "Français" },
{ value: "gl", label: "Galego" },
{ value: "he", label: "עברית" },
{ value: "hi", label: "हिन्दी" },
{ value: "hu", label: "Magyar" },
{ value: "id", label: "Bahasa Indonesia" },
{ value: "is", label: "íslenska" },
{ value: "it", label: "Italiano" },
{ value: "ja", label: "日本語" },
{ value: "kab", label: "Taqbaylit" },
{ value: "ko", label: "한국어" },
{ value: "lo", label: "ລາວ" },
{ value: "lt", label: "Lietuvių" },
{ value: "lv", label: "Latviešu" },
{ value: "nb_NO", label: "Norwegian Bokmål" },
{ value: "nl", label: "Nederlands" },
{ value: "nn", label: "Norsk Nynorsk" },
{ value: "pl", label: "Polski" },
{ value: "pt", label: "Português" },
{ value: "pt_BR", label: "Português do Brasil" },
{ value: "ru", label: "Русский" },
{ value: "sk", label: "Slovenčina" },
{ value: "sq", label: "Shqip" },
{ value: "sr", label: "српски" },
{ value: "sv", label: "Svenska" },
{ value: "te", label: "తెలుగు" },
{ value: "th", label: "ไทย" },
{ value: "tr", label: "Türkçe" },
{ value: "uk", label: "українська мова" },
{ value: "vi", label: "Tiếng Việt" },
{ value: "vls", label: "West-Vlaams" },
{ value: "zh_Hans", label: "简体中文" }, // simplified chinese
{ value: "zh_Hant", label: "繁體中文" }, // traditional chinese
];
// cpx includes globbed parts of the filename in the destination, but excludes
// common parents. Hence, "res/{a,b}/**": the output will be "dest/a/..." and
// "dest/b/...".
const COPY_LIST = [
["res/apple-app-site-association", "webapp"],
["res/manifest.json", "webapp"],
["res/sw.js", "webapp"],
["res/welcome.html", "webapp"],
["res/welcome/**", "webapp/welcome"],
["res/themes/**", "webapp/themes"],
["res/vector-icons/**", "webapp/vector-icons"],
["res/decoder-ring/**", "webapp/decoder-ring"],
["node_modules/matrix-react-sdk/res/media/**", "webapp/media"],
["node_modules/@matrix-org/olm/olm_legacy.js", "webapp", { directwatch: 1 }],
["./config.json", "webapp", { directwatch: 1 }],
["contribute.json", "webapp"],
];
const parseArgs = require("minimist");
const Cpx = require("cpx");
const chokidar = require("chokidar");
const fs = require("fs");
const rimraf = require("rimraf");
const argv = parseArgs(process.argv.slice(2), {});
const watch = argv.w;
const verbose = argv.v;
function errCheck(err) {
if (err) {
console.error(err.message);
process.exit(1);
}
}
// Check if webapp exists
if (!fs.existsSync("webapp")) {
fs.mkdirSync("webapp");
}
// Check if i18n exists
if (!fs.existsSync("webapp/i18n/")) {
fs.mkdirSync("webapp/i18n/");
}
function next(i, err) {
errCheck(err);
if (i >= COPY_LIST.length) {
return;
}
const ent = COPY_LIST[i];
const source = ent[0];
const dest = ent[1];
const opts = ent[2] || {};
let cpx = undefined;
if (!opts.lang) {
cpx = new Cpx.Cpx(source, dest);
}
if (verbose && cpx) {
cpx.on("copy", (event) => {
console.log(`Copied: ${event.srcPath} --> ${event.dstPath}`);
});
cpx.on("remove", (event) => {
console.log(`Removed: ${event.path}`);
});
}
const cb = (err) => {
next(i + 1, err);
};
if (watch) {
if (opts.directwatch) {
// cpx -w creates a watcher for the parent of any files specified,
// which in the case of config.json is '.', which inevitably takes
// ages to crawl. So we create our own watcher on the files
// instead.
const copy = () => {
cpx.copy(errCheck);
};
chokidar.watch(source).on("add", copy).on("change", copy).on("ready", cb).on("error", errCheck);
} else {
cpx.on("watch-ready", cb);
cpx.on("watch-error", cb);
cpx.watch();
}
} else {
cpx.copy(cb);
}
}
function genLangFile(lang, dest) {
const reactSdkFile = "node_modules/matrix-react-sdk/src/i18n/strings/" + lang + ".json";
const riotWebFile = "src/i18n/strings/" + lang + ".json";
let translations = {};
[reactSdkFile, riotWebFile].forEach(function (f) {
if (fs.existsSync(f)) {
try {
Object.assign(translations, JSON.parse(fs.readFileSync(f).toString()));
} catch (e) {
console.error("Failed: " + f, e);
throw e;
}
}
});
translations = weblateToCounterpart(translations);
const json = JSON.stringify(translations, null, 4);
const jsonBuffer = Buffer.from(json);
const digest = loaderUtils.getHashDigest(jsonBuffer, null, null, 7);
const filename = `${lang}.${digest}.json`;
fs.writeFileSync(dest + filename, json);
if (verbose) {
console.log("Generated language file: " + filename);
}
return filename;
}
function genLangList(langFileMap) {
const languages = {};
INCLUDE_LANGS.forEach(function (lang) {
const normalizedLanguage = lang.value.toLowerCase().replace("_", "-");
const languageParts = normalizedLanguage.split("-");
if (languageParts.length == 2 && languageParts[0] == languageParts[1]) {
languages[languageParts[0]] = { fileName: langFileMap[lang.value], label: lang.label };
} else {
languages[normalizedLanguage] = { fileName: langFileMap[lang.value], label: lang.label };
}
});
fs.writeFile("webapp/i18n/languages.json", JSON.stringify(languages, null, 4), function (err) {
if (err) {
console.error("Copy Error occured: " + err);
throw new Error("Failed to generate languages.json");
}
});
if (verbose) {
console.log("Generated languages.json");
}
}
/**
* Convert translation key from weblate format
* (which only supports a single level) to counterpart
* which requires object values for 'count' translations.
*
* eg.
* "there are %(count)s badgers|one": "a badger",
* "there are %(count)s badgers|other": "%(count)s badgers"
* becomes
* "there are %(count)s badgers": {
* "one": "a badger",
* "other": "%(count)s badgers"
* }
*/
function weblateToCounterpart(inTrs) {
const outTrs = {};
for (const key of Object.keys(inTrs)) {
const keyParts = key.split("|", 2);
if (keyParts.length === 2) {
let obj = outTrs[keyParts[0]];
if (obj === undefined) {
obj = outTrs[keyParts[0]] = {};
} else if (typeof obj === "string") {
// This is a transitional edge case if a string went from singular to pluralised and both still remain
// in the translation json file. Use the singular translation as `other` and merge pluralisation atop.
obj = outTrs[keyParts[0]] = {
other: inTrs[key],
};
console.warn("Found entry in i18n file in both singular and pluralised form", keyParts[0]);
}
obj[keyParts[1]] = inTrs[key];
} else {
outTrs[key] = inTrs[key];
}
}
return outTrs;
}
/**
watch the input files for a given language,
regenerate the file, adding its content-hashed filename to langFileMap
and regenerating languages.json with the new filename
*/
function watchLanguage(lang, dest, langFileMap) {
const reactSdkFile = "node_modules/matrix-react-sdk/src/i18n/strings/" + lang + ".json";
const riotWebFile = "src/i18n/strings/" + 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
// appears empty. Possibly https://github.com/nodejs/node/issues/6112
let makeLangDebouncer;
const makeLang = () => {
if (makeLangDebouncer) {
clearTimeout(makeLangDebouncer);
}
makeLangDebouncer = setTimeout(() => {
const filename = genLangFile(lang, dest);
langFileMap[lang] = filename;
genLangList(langFileMap);
}, 500);
};
[reactSdkFile, riotWebFile].forEach(function (f) {
chokidar.watch(f).on("add", makeLang).on("change", makeLang).on("error", errCheck);
});
}
// language resources
const I18N_DEST = "webapp/i18n/";
const I18N_FILENAME_MAP = INCLUDE_LANGS.reduce((m, l) => {
const filename = genLangFile(l.value, I18N_DEST);
m[l.value] = filename;
return m;
}, {});
genLangList(I18N_FILENAME_MAP);
if (watch) {
INCLUDE_LANGS.forEach((l) => watchLanguage(l.value, I18N_DEST, I18N_FILENAME_MAP));
}
// non-language resources
next(0);

150
scripts/copy-res.ts Executable file
View File

@ -0,0 +1,150 @@
#!/usr/bin/env node
// copies the resources into the webapp directory.
import parseArgs from "minimist";
import * as chokidar from "chokidar";
import * as fs from "node:fs";
import _ from "lodash";
import { util } from "webpack";
import { Translations } from "matrix-web-i18n";
const REACT_I18N_BASE_PATH = "node_modules/matrix-react-sdk/src/i18n/strings/";
const I18N_BASE_PATH = "src/i18n/strings/";
const INCLUDE_LANGS = [...new Set([...fs.readdirSync(I18N_BASE_PATH), ...fs.readdirSync(REACT_I18N_BASE_PATH)])]
.filter((fn) => fn.endsWith(".json"))
.map((f) => f.slice(0, -5));
const argv = parseArgs(process.argv.slice(2), {});
const watch = argv.w;
const verbose = argv.v;
function errCheck(err?: Error): void {
if (err) {
console.error(err.message);
process.exit(1);
}
}
// Check if webapp exists
if (!fs.existsSync("webapp")) {
fs.mkdirSync("webapp");
}
// Check if i18n exists
if (!fs.existsSync("webapp/i18n/")) {
fs.mkdirSync("webapp/i18n/");
}
const logWatch = (path: string) => {
if (verbose) {
console.log(`Watching: ${path}`);
}
};
function prepareLangFile(lang: string, dest: string): [filename: string, json: string] {
const reactSdkFile = REACT_I18N_BASE_PATH + lang + ".json";
const riotWebFile = I18N_BASE_PATH + lang + ".json";
let translations: Translations = {};
[reactSdkFile, riotWebFile].forEach(function (f) {
if (fs.existsSync(f)) {
try {
translations = _.merge(translations, JSON.parse(fs.readFileSync(f).toString()));
} catch (e) {
console.error("Failed: " + f, e);
throw e;
}
}
});
const json = JSON.stringify(translations, null, 4);
const jsonBuffer = Buffer.from(json);
const digest = util.createHash("xxhash64").update(jsonBuffer).digest("hex").slice(0, 7);
const filename = `${lang}.${digest}.json`;
return [filename, json];
}
function genLangFile(dest: string, filename: string, json: string) {
fs.writeFileSync(dest + filename, json);
if (verbose) {
console.log("Generated language file: " + filename);
}
}
function genLangList(langFileMap: Record<string, string>): void {
const languages: Record<string, string> = {};
INCLUDE_LANGS.forEach(function (lang) {
const normalizedLanguage = lang.toLowerCase().replace("_", "-");
const languageParts = normalizedLanguage.split("-");
if (languageParts.length == 2 && languageParts[0] == languageParts[1]) {
languages[languageParts[0]] = langFileMap[lang];
} else {
languages[normalizedLanguage] = langFileMap[lang];
}
});
fs.writeFile("webapp/i18n/languages.json", JSON.stringify(languages, null, 4), function (err) {
if (err) {
console.error("Copy Error occured: " + err.message);
throw new Error("Failed to generate languages.json");
}
});
if (verbose) {
console.log("Generated languages.json");
}
}
/*
* watch the input files for a given language,
* regenerate the file, adding its content-hashed filename to langFileMap
* and regenerating languages.json with the new filename
*/
function watchLanguage(lang: string, dest: string, langFileMap: Record<string, string>): void {
const reactSdkFile = REACT_I18N_BASE_PATH + lang + ".json";
const riotWebFile = 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
// appears empty. Possibly https://github.com/nodejs/node/issues/6112
let makeLangDebouncer: ReturnType<typeof setTimeout>;
const makeLang = (): void => {
if (makeLangDebouncer) {
clearTimeout(makeLangDebouncer);
}
makeLangDebouncer = setTimeout(() => {
const [filename, json] = prepareLangFile(lang, dest);
genLangFile(dest, filename, json);
langFileMap[lang] = filename;
genLangList(langFileMap);
}, 500);
};
[reactSdkFile, riotWebFile].forEach(function (f) {
chokidar
.watch(f, { ignoreInitial: true })
.on("ready", () => {
logWatch(f);
})
.on("add", makeLang)
.on("change", makeLang)
.on("error", errCheck);
});
}
// 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);
if (!watch) {
genLangFile(I18N_DEST, filename, json);
}
m[l] = filename;
return m;
}, {});
if (watch) {
INCLUDE_LANGS.forEach((l) => watchLanguage(l, I18N_DEST, I18N_FILENAME_MAP));
} else {
genLangList(I18N_FILENAME_MAP);
}

635
scripts/gen-workflow-mermaid.ts Executable file
View File

@ -0,0 +1,635 @@
#!/usr/bin/env -S npx ts-node
import fs from "node:fs";
import path from "node:path";
import YAML from "yaml";
import parseArgs from "minimist";
import cronstrue from "cronstrue";
import { partition } from "lodash";
const argv = parseArgs<{
debug: boolean;
on: string | string[];
}>(process.argv.slice(2), {
string: ["on"],
boolean: ["debug"],
});
/**
* Generates unique ID strings (incremental base36) representing the given inputs.
*/
class IdGenerator<T> {
private id = 0;
private map = new Map<T, string>();
public get(s: T): string {
if (this.map.has(s)) return this.map.get(s)!;
const id = "ID" + this.id.toString(36).toLowerCase();
this.map.set(s, id);
this.id++;
return id;
}
public debug(): void {
console.log("```");
console.log(this.map);
console.log("```");
}
}
/**
* Type representing a node on a graph with additional metadata
*/
interface Node {
// Workflows are keyed by project/name??id
// Jobs are keyed by id
// Triggers are keyed by id
id: string;
name: string;
shape:
| "round edges"
| "stadium"
| "subroutine"
| "cylinder"
| "circle"
| "flag"
| "rhombus"
| "hexagon"
| "parallelogram"
| "parallelogram_alt"
| "trapezoid"
| "trapezoid_alt"
| "double_circle";
link?: string;
}
/**
* Type representing a directed edge on a graph with an optional label
*/
type Edge<T> = [source: T, destination: T, label?: string];
class Graph<T extends Node> {
public nodes = new Map<string, T>();
public edges: Edge<T>[] = [];
public addNode(node: T): void {
if (!this.nodes.has(node.id)) {
this.nodes.set(node.id, node);
}
}
public removeNode(node: T): Edge<T>[] {
if (!this.nodes.has(node.id)) return [];
this.nodes.delete(node.id);
const [removedEdges, keptEdges] = partition(
this.edges,
([source, destination]) => source === node || destination === node,
);
this.edges = keptEdges;
return removedEdges;
}
public addEdge(source: T, destination: T, label?: string): void {
if (this.edges.some(([_source, _destination]) => _source === source && _destination === destination)) return;
this.edges.push([source, destination, label]);
}
// Removes nodes without any edges
public cull(): void {
const seenNodes = new Set<Node>();
graph.edges.forEach(([source, destination]) => {
seenNodes.add(source);
seenNodes.add(destination);
});
graph.nodes.forEach((node) => {
if (!seenNodes.has(node)) {
graph.nodes.delete(node.id);
}
});
}
public get roots(): Set<T> {
const roots = new Set(this.nodes.values());
this.edges.forEach(([source, destination]) => {
roots.delete(destination);
});
return roots;
}
private componentsRecurse(root: T, visited: Set<T>): T[] {
if (visited.has(root)) return [root];
visited.add(root);
const neighbours = [root];
this.edges.forEach(([source, destination]) => {
if (source === root) {
neighbours.push(...this.componentsRecurse(destination, visited));
} else if (destination === root) {
neighbours.push(...this.componentsRecurse(source, visited));
}
});
return neighbours;
}
public get components(): Graph<T>[] {
const graphs: Graph<T>[] = [];
const visited = new Set<T>();
this.nodes.forEach((node) => {
if (visited.has(node)) return;
const graph = new Graph<T>();
graphs.push(graph);
const nodes = this.componentsRecurse(node, visited);
nodes.forEach((node) => {
graph.addNode(node);
this.edges.forEach((edge) => {
if (edge[0] === node || edge[1] === node) {
graph.addEdge(...edge);
}
});
});
});
return graphs;
}
}
/**
* Type representing a GitHub project
*/
interface Project {
url: string;
name: string;
path: string;
workflows: Map<string, Workflow>;
}
/**
* Type representing a GitHub Actions Workflow
*/
interface Workflow extends Node {
path: string;
project: Project;
jobs: Job[];
on: WorkflowYaml["on"];
}
/**
* Type representing a job within a GitHub Actions Workflow
*/
interface Job extends Node {
jobId: string; // id relative to workflow
needs?: string[];
strategy?: {
matrix: {
[key: string]: string[];
} & {
include?: Record<string, string>[];
exclude?: Record<string, string>[];
};
};
}
/**
* Type representing the YAML structure of a GitHub Actions Workflow file
*/
interface WorkflowYaml {
name: string;
on: {
workflow_run?: {
workflows: string[];
}; // Magic
workflow_call?: {}; // Reusable
workflow_dispatch?: {}; // Manual
pull_request?: {};
merge_group?: {};
push?: {
tags?: string[];
branches?: string[];
};
schedule?: { cron: string }[];
release?: {};
//
label?: {};
issues?: {};
};
jobs: {
[job: string]: {
name?: string;
needs?: string | string[];
strategy?: Job["strategy"];
};
};
}
/**
* Type representing a trigger of a GitHub Actions Workflow
*/
type Trigger = Node;
// TODO workflow_call reusables
/* eslint-disable @typescript-eslint/naming-convention */
const TRIGGERS: {
[key in keyof WorkflowYaml["on"]]: (
data: NonNullable<WorkflowYaml["on"][key]>,
workflow: Workflow,
) => Trigger | Trigger[];
} = {
workflow_dispatch: () => ({
id: "on:workflow_dispatch",
name: "Manual",
shape: "circle",
}),
issues: (_, { project }) => ({ id: `on:issues/${project.name}`, name: `${project.name} Issues`, shape: "circle" }),
label: (_, { project }) => ({ id: "on:label", name: "on: Label", shape: "circle" }),
release: (_, { project }) => ({
id: `on:release/${project.name}`,
name: `${project.name} Release`,
shape: "circle",
}),
push: (data, { project }) => {
const nodes: Trigger[] = [];
data.tags?.forEach((tag) => {
const name = `Push ${project.name}<br>tag ${tag}`;
nodes.push({ id: `on:push/${project.name}/tag/${tag}`, name, shape: "circle" });
});
data.branches?.forEach((branch) => {
const name = `Push ${project.name}<br>${branch}`;
nodes.push({ id: `on:push/${project.name}/branch/${branch}`, name, shape: "circle" });
});
return nodes;
},
schedule: (data) =>
data.map(({ cron }) => ({
id: `on:schedule/${cron}`,
name: cronstrue.toString(cron).replaceAll(", ", "<br>"),
shape: "circle",
})),
pull_request: (_, { project }) => ({
id: `on:pull_request/${project.name}`,
name: `Pull Request<br>${project.name}`,
shape: "circle",
}),
// TODO should we be just dropping these?
workflow_run: (data) => data.workflows.map((parent) => workflows.get(parent)).filter(Boolean) as Workflow[],
};
/* eslint-enable @typescript-eslint/naming-convention */
const triggers = new Map<string, Trigger>(); // keyed by trigger id
const projects = new Map<string, Project>(); // keyed by project name
const workflows = new Map<string, Workflow>(); // keyed by workflow name
function getTriggerNodes<K extends keyof WorkflowYaml["on"]>(key: K, workflow: Workflow): Trigger[] {
if (!TRIGGERS[key]) return [];
if ((typeof argv.on === "string" || Array.isArray(argv.on)) && !toArray(argv.on).includes(key)) {
return [];
}
const data = workflow.on[key]!;
const nodes = toArray(TRIGGERS[key]!(data, workflow));
return nodes.map((node) => {
if (triggers.has(node.id)) return triggers.get(node.id)!;
triggers.set(node.id, node);
return node;
});
}
function readFile(...pathSegments: string[]): string {
return fs.readFileSync(path.join(...pathSegments), { encoding: "utf-8" });
}
function readJson<T extends object>(...pathSegments: string[]): T {
return JSON.parse(readFile(...pathSegments));
}
function readYaml<T extends object>(...pathSegments: string[]): T {
return YAML.parse(readFile(...pathSegments));
}
function toArray<T>(v: T | T[]): T[] {
return Array.isArray(v) ? v : [v];
}
function cartesianProduct<T>(sets: T[][]): T[][] {
return sets.reduce<T[][]>(
(results, ids) =>
results
.map((result) => ids.map((id) => [...result, id]))
.reduce((nested, result) => [...nested, ...result]),
[[]],
);
}
function shallowCompare(obj1: Record<string, any>, obj2: Record<string, any>): boolean {
return (
Object.keys(obj1).length === Object.keys(obj2).length &&
Object.keys(obj1).every((key) => obj1[key] === obj2[key])
);
}
// Data ingest
for (const projectPath of argv._) {
const {
name,
repository: { url },
} = readJson<{ name: string; repository: { url: string } }>(projectPath, "package.json");
const workflowsPath = path.join(projectPath, ".github", "workflows");
const project: Project = {
name,
url,
path: projectPath,
workflows: new Map(),
};
for (const file of fs.readdirSync(workflowsPath).filter((f) => f.endsWith(".yml") || f.endsWith(".yaml"))) {
const data = readYaml<WorkflowYaml>(workflowsPath, file);
const name = data.name ?? file;
const workflow: Workflow = {
id: `${project.name}/${name}`,
name,
shape: "hexagon",
path: path.join(workflowsPath, file),
project,
link: `${project.url}/blob/develop/.github/workflows/${file}`,
on: data.on,
jobs: [],
};
for (const jobId in data.jobs) {
const job = data.jobs[jobId];
workflow.jobs.push({
id: `${workflow.name}/${jobId}`,
jobId,
name: job.name ?? jobId,
strategy: job.strategy,
needs: job.needs ? toArray(job.needs) : undefined,
shape: "subroutine",
link: `${project.url}/blob/develop/.github/workflows/${file}`,
});
}
project.workflows.set(name, workflow);
workflows.set(name, workflow);
}
projects.set(name, project);
}
class MermaidFlowchartPrinter {
private static INDENT = 4;
private currentIndent = 0;
private text = "";
public readonly idGenerator = new IdGenerator();
private print(text: string): void {
this.text += " ".repeat(this.currentIndent) + text + "\n";
}
public finish(): void {
this.indent(-1);
if (this.markdown) this.print("```\n");
console.log(this.text);
}
private indent(delta = 1): void {
this.currentIndent += delta * MermaidFlowchartPrinter.INDENT;
}
public constructor(direction: "TD" | "TB" | "BT" | "RL" | "LR", title?: string, private readonly markdown = false) {
if (this.markdown) {
this.print("```mermaid");
}
// Print heading
if (title) {
this.print("---");
this.print(`title: ${title}`);
this.print("---");
}
this.print(`flowchart ${direction}`);
this.indent();
}
public subgraph(id: string, name: string, fn: () => void): void {
this.print(`subgraph ${this.idGenerator.get(id)}["${name}"]`);
this.indent();
fn();
this.indent(-1);
this.print("end");
}
public node(node: Node): void {
const id = this.idGenerator.get(node.id);
const name = node.name.replaceAll('"', "'");
switch (node.shape) {
case "round edges":
this.print(`${id}("${name}")`);
break;
case "stadium":
this.print(`${id}(["${name}"])`);
break;
case "subroutine":
this.print(`${id}[["${name}"]]`);
break;
case "cylinder":
this.print(`${id}[("${name}")]`);
break;
case "circle":
this.print(`${id}(("${name}"))`);
break;
case "flag":
this.print(`${id}>"${name}"]`);
break;
case "rhombus":
this.print(`${id}{"${name}"}`);
break;
case "hexagon":
this.print(`${id}{{"${name}"}}`);
break;
case "parallelogram":
this.print(`${id}[/"${name}"/]`);
break;
case "parallelogram_alt":
this.print(`${id}[\\"${name}"\\]`);
break;
case "trapezoid":
this.print(`${id}[/"${name}"\\]`);
break;
case "trapezoid_alt":
this.print(`${id}[\\"${name}"/]`);
break;
case "double_circle":
this.print(`${id}((("${name}")))`);
break;
}
if (node.link) {
this.print(`click ${id} href "${node.link}" "Click to open workflow"`);
}
}
public edge(source: Node, destination: Node, text?: string): void {
const sourceId = this.idGenerator.get(source.id);
const destinationId = this.idGenerator.get(destination.id);
if (text) {
this.print(`${sourceId}-- ${text} -->${destinationId}`);
} else {
this.print(`${sourceId} --> ${destinationId}`);
}
}
}
const graph = new Graph<Workflow | Node>();
for (const workflow of workflows.values()) {
if (
(typeof argv.on === "string" || Array.isArray(argv.on)) &&
!toArray(argv.on).some((trigger) => trigger in workflow.on)
) {
continue;
}
graph.addNode(workflow);
Object.keys(workflow.on).forEach((trigger) => {
const nodes = getTriggerNodes(trigger as keyof WorkflowYaml["on"], workflow);
nodes.forEach((node) => {
graph.addNode(node);
graph.addEdge(node, workflow, "project" in node ? "workflow_run" : undefined);
});
});
}
// TODO separate disconnected nodes into their own graph
graph.cull();
// This is an awful hack to make the output graphs much better by allowing the splitting of certain nodes //
const bifurcatedNodes = [triggers.get("on:workflow_dispatch")].filter(Boolean) as Node[];
const removedEdgeMap = new Map<Node, Edge<any>[]>();
for (const node of bifurcatedNodes) {
removedEdgeMap.set(node, graph.removeNode(node));
}
const components = graph.components;
for (const node of bifurcatedNodes) {
const removedEdges = removedEdgeMap.get(node)!;
components.forEach((graph) => {
removedEdges.forEach((edge) => {
if (graph.nodes.has(edge[1].id)) {
graph.addNode(node);
graph.addEdge(...edge);
}
});
});
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////
if (argv.debug) {
debugGraph("global", graph);
}
components.forEach((graph) => {
const title = [...graph.roots]
.map((root) => root.name)
.join(" & ")
.replaceAll("<br>", " ");
const printer = new MermaidFlowchartPrinter("LR", title, true);
graph.nodes.forEach((node) => {
if ("project" in node) {
// TODO unsure about this edge
// if (node.jobs.length === 1) {
// printer.node(node);
// return;
// }
// TODO handle job.if on github.event_name
const subgraph = new Graph<Job>();
for (const job of node.jobs) {
subgraph.addNode(job);
if (job.needs) {
toArray(job.needs).forEach((req) => {
subgraph.addEdge(node.jobs.find((job) => job.jobId === req)!, job, "needs");
});
}
}
printer.subgraph(node.id, node.name, () => {
subgraph.edges.forEach(([source, destination, text]) => {
printer.edge(source, destination, text);
});
subgraph.nodes.forEach((job) => {
if (!job.strategy?.matrix) {
printer.node(job);
return;
}
let variations = cartesianProduct(
Object.keys(job.strategy.matrix)
.filter((key) => key !== "include" && key !== "exclude")
.map((matrixKey) => {
return job.strategy!.matrix[matrixKey].map((value) => ({ [matrixKey]: value }));
}),
)
.map((variation) => Object.assign({}, ...variation))
.filter((variation) => Object.keys(variation).length > 0);
if (job.strategy.matrix.include) {
variations.push(...job.strategy.matrix.include);
}
job.strategy.matrix.exclude?.forEach((exclusion) => {
variations = variations.filter((variation) => {
return !shallowCompare(exclusion, variation);
});
});
// TODO validate edge case
if (variations.length === 0) {
printer.node(job);
return;
}
const jobName = job.name.replace(/\${{.+}}/g, "").replace(/(?:\(\)| )+/g, " ");
printer.subgraph(job.id, jobName, () => {
variations.forEach((variation, i) => {
let variationName = job.name;
if (variationName.includes("${{ matrix.")) {
Object.keys(variation).map((key) => {
variationName = variationName.replace(`\${{ matrix.${key} }}`, variation[key]);
});
} else {
variationName = `${variationName} (${Object.values(variation).join(", ")})`;
}
printer.node({ ...job, id: `${job.id}-variation-${i}`, name: variationName });
});
});
});
});
return;
}
printer.node(node);
});
graph.edges.forEach(([sourceName, destinationName, text]) => {
printer.edge(sourceName, destinationName, text);
});
printer.finish();
if (argv.debug) {
printer.idGenerator.debug();
debugGraph("subgraph", graph);
}
});
function debugGraph(name: string, graph: Graph<any>): void {
console.log("```");
console.log(`## ${name}`);
console.log(new Map(graph.nodes));
console.log(graph.edges.map((edge) => ({ source: edge[0].id, destination: edge[1].id, text: edge[2] })));
console.log("```");
console.log("");
}

View File

@ -55,12 +55,6 @@ declare global {
on(channel: ElectronChannel, listener: (event: Event, ...args: any[]) => void): void;
send(channel: ElectronChannel, ...args: any[]): void;
}
interface Navigator {
// PWA badging extensions https://w3c.github.io/badging/
setAppBadge?(count: number): Promise<void>;
clearAppBadge?(): Promise<void>;
}
}
// add method which is missing from the node typing

View File

@ -14,13 +14,14 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import * as React from "react";
import { _t } from "matrix-react-sdk/src/languageHandler";
import React, { ReactNode } from "react";
import SdkConfig from "matrix-react-sdk/src/SdkConfig";
import { _t } from "../../languageHandler";
// directly import the style here as this layer does not support rethemedex at this time so no matrix-react-sdk
// PostCSS variables will be accessible.
import "../../../res/css/structures/ErrorView.pcss";
import { ReactNode } from "react";
interface IProps {
onAccept(): void;
@ -39,12 +40,7 @@ const CompatibilityView: React.FC<IProps> = ({ onAccept }) => {
<p>
<strong>iOS</strong> (iPhone or iPad)
</p>
<a
href={iosCustomUrl || "https://apps.apple.com/app/vector/id1083446067"}
target="_blank"
rel="noreferrer noopener"
className="mx_ClearDecoration"
>
<a href={iosCustomUrl} target="_blank" rel="noreferrer noopener" className="mx_ClearDecoration">
<img height="48" src="themes/element/img/download/apple.svg" alt="Apple App Store" />
</a>
</>
@ -62,7 +58,7 @@ const CompatibilityView: React.FC<IProps> = ({ onAccept }) => {
// undefined or string
android.push(
<a
href={andCustomUrl || "https://play.google.com/store/apps/details?id=im.vector.app"}
href={andCustomUrl}
target="_blank"
rel="noreferrer noopener"
className="mx_ClearDecoration"
@ -76,7 +72,7 @@ const CompatibilityView: React.FC<IProps> = ({ onAccept }) => {
// undefined or string
android.push(
<a
href={fdroidCustomUrl || "https://f-droid.org/repository/browse/?fdid=im.vector.app"}
href={fdroidCustomUrl}
target="_blank"
rel="noreferrer noopener"
className="mx_ClearDecoration"
@ -91,7 +87,7 @@ const CompatibilityView: React.FC<IProps> = ({ onAccept }) => {
android = [];
}
let mobileHeader: ReactNode = <h2 id="step2_heading">{_t("Use %(brand)s on mobile", { brand })}</h2>;
let mobileHeader: ReactNode = <h2 id="step2_heading">{_t("use_brand_on_mobile", { brand })}</h2>;
if (!android.length && !ios) {
mobileHeader = null;
}
@ -103,24 +99,17 @@ const CompatibilityView: React.FC<IProps> = ({ onAccept }) => {
<span className="mx_HomePage_logo">
<img height="42" src="themes/element/img/logos/element-logo.svg" alt="Element" />
</span>
<h1>{_t("Unsupported browser")}</h1>
<h1>{_t("incompatible_browser|title")}</h1>
</div>
<div className="mx_HomePage_col">
<div className="mx_HomePage_row">
<div>
<h2 id="step1_heading">{_t("Your browser can't run %(brand)s", { brand })}</h2>
<h2 id="step1_heading">{_t("incompatible_browser|summary", { brand })}</h2>
<p>{_t("incompatible_browser|features", { brand })}</p>
<p>
{_t(
"%(brand)s uses advanced browser features which aren't " +
"supported by your current browser.",
{ brand },
)}
</p>
<p>
{_t(
"Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, " +
"or <safariLink>Safari</safariLink> for the best experience.",
"incompatible_browser|browser_links",
{},
{
chromeLink: (sub) => <a href="https://www.google.com/chrome">{sub}</a>,
@ -129,13 +118,8 @@ const CompatibilityView: React.FC<IProps> = ({ onAccept }) => {
},
)}
</p>
<p>
{_t(
"You can continue using your current browser, but some or all features may not work " +
"and the look and feel of the application may be incorrect.",
)}
</p>
<button onClick={onAccept}>{_t("I understand the risks and wish to continue")}</button>
<p>{_t("incompatible_browser|feature_warning")}</p>
<button onClick={onAccept}>{_t("incompatible_browser|continue_warning")}</button>
</div>
</div>
</div>
@ -153,7 +137,7 @@ const CompatibilityView: React.FC<IProps> = ({ onAccept }) => {
<div className="mx_HomePage_row mx_Center mx_Spacer">
<p className="mx_Spacer">
<a href="https://element.io" target="_blank" className="mx_FooterLink">
{_t("Go to element.io")}
{_t("go_to_element_io")}
</a>
</p>
</div>

View File

@ -15,7 +15,8 @@ limitations under the License.
*/
import * as React from "react";
import { _t } from "matrix-react-sdk/src/languageHandler";
import { _t } from "../../languageHandler";
// directly import the style here as this layer does not support rethemedex at this time so no matrix-react-sdk
// PostCSS variables will be accessible.
@ -35,7 +36,7 @@ const ErrorView: React.FC<IProps> = ({ title, messages }) => {
<span className="mx_HomePage_logo">
<img height="42" src="themes/element/img/logos/element-logo.svg" alt="Element" />
</span>
<h1>{_t("Failed to start")}</h1>
<h1>{_t("failed_to_start")}</h1>
</div>
<div className="mx_HomePage_col">
<div className="mx_HomePage_row">
@ -48,7 +49,7 @@ const ErrorView: React.FC<IProps> = ({ title, messages }) => {
<div className="mx_HomePage_row mx_Center mx_Spacer">
<p className="mx_Spacer">
<a href="https://element.io" target="_blank" className="mx_FooterLink">
{_t("Go to element.io")}
{_t("go_to_element_io")}
</a>
</p>
</div>

View File

@ -17,7 +17,8 @@ limitations under the License.
import React, { ReactElement } from "react";
import SdkConfig from "matrix-react-sdk/src/SdkConfig";
import { _t } from "matrix-react-sdk/src/languageHandler";
import { _t } from "../../../languageHandler";
const VectorAuthFooter = (): ReactElement => {
const brandingConfig = SdkConfig.getObject("branding");
@ -40,7 +41,7 @@ const VectorAuthFooter = (): ReactElement => {
<footer className="mx_AuthFooter" role="contentinfo">
{authFooterLinks}
<a href="https://matrix.org" target="_blank" rel="noreferrer noopener">
{_t("Powered by Matrix")}
{_t("powered_by_matrix")}
</a>
</footer>
);

View File

@ -1,31 +0,0 @@
{
"Dismiss": "أهمِل",
"Unknown device": "جهاز مجهول",
"Welcome to Element": "مرحبًا بك في Element",
"Create Account": "أنشِئ حسابًا",
"Explore rooms": "استكشِف الغرف",
"Sign In": "لِج",
"Invalid configuration: no default server specified.": "الضبط غير صالح: لم تحدّد خادومًا مبدئيًا.",
"Your Element is misconfigured": "لم يُضبط تطبيق Element كما ينبغي",
"Your Element configuration contains invalid JSON. Please correct the problem and reload the page.": "يحتوي ضبط تطبيق Element على تنسيق JSON غير صالح. من فضلك صحّح المشكلة وأعِد تحميل الصفحة.",
"The message from the parser is: %(message)s": "الرسالة القادمة من المحلّل: %(message)s",
"Invalid JSON": "تنسيق JSON غير صالح",
"Unable to load config file: please refresh the page to try again.": "تعذّر تحميل ملف الضبط: من فضلك أنعِش الصفحة لمعاودة المحاولة.",
"Unexpected error preparing the app. See console for details.": "حدث عُطل غير متوقع أثناء تجهيز التطبيق. طالِع المِعراض للتفاصيل.",
"Download Completed": "اكتمل التنزيل",
"Open": "افتح",
"Go to your browser to complete Sign In": "افتح المتصفح لإكمال الولوج",
"Unsupported browser": "متصفح غير مدعوم",
"Your browser can't run %(brand)s": "لا يمكن لمتصفحك تشغيل %(brand)s",
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "يستعمل %(brand)s ميزات متقدمة في المتصفحات لا يدعمها متصفحك الحالي.",
"Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> for the best experience.": "من فضلك ثبّت <chromeLink>كروم</chromeLink> أو <firefoxLink>فَيَرفُكس</firefoxLink> أو <safariLink>سفاري</safariLink> لأفضل تجربة.",
"You can continue using your current browser, but some or all features may not work and the look and feel of the application may be incorrect.": "يمكنك مواصل استعمال متصفحك الحالي ولكن قد لا تعمل بعض المزايا (أو كلها) وقد لا يظهر التطبيق كما ينبغي له أن يظهر.",
"I understand the risks and wish to continue": "أفهم المخاطرة وأود المواصلة",
"Go to element.io": "انتقل إلى element.io",
"Failed to start": "فشل البدء",
"Powered by Matrix": "تدعمه «ماترِكس»",
"Use %(brand)s on mobile": "استعمل %(brand)s على المحمول",
"Decentralised, encrypted chat &amp; collaboration powered by $matrixLogo": "محادثة لامركزية، مشفرة &amp; تعمل بواسطة $matrixLogo",
"%(appName)s: %(browserName)s on %(osName)s": "%(appName)s: %(browserName)s في %(osName)s",
"%(brand)s Desktop: %(platformName)s": "%(brand)s سطح المكتب %(platformName)s"
}

View File

@ -1,29 +0,0 @@
{
"Unknown device": "Naməlum qurğu",
"Invalid JSON": "Yanlış JSON",
"Sign In": "Daxil ol",
"Create Account": "Hesab Aç",
"Explore rooms": "Otaqları kəşf edin",
"Unexpected error preparing the app. See console for details.": "Tətbiqin başladılmasında gözlənilməz xəta.Təfərrüatlar üçün konsola baxın.",
"Invalid configuration: no default server specified.": "Yanlış konfiqurasiya: standart server göstərilməyib.",
"The message from the parser is: %(message)s": "Sözügedən mesaj: %(message)s",
"Dismiss": "Nəzərə almayın",
"Welcome to Element": "Element-ə xoş gəlmişsiniz",
"Decentralised, encrypted chat &amp; collaboration powered by $matrixLogo": "$matrixLogo tərəfindən dəstəklənən mərkəzləşdirilməmiş ,şifrələnmiş söhbət &amp; əməkdaşlıq",
"Failed to start": "Başlatmaq alınmadı",
"Go to element.io": "element.io saytına keçin",
"I understand the risks and wish to continue": "Mən riskləri başa düşürəm və davam etmək istəyirəm",
"You can continue using your current browser, but some or all features may not work and the look and feel of the application may be incorrect.": "Siz cari brauzerinizdən istifadə etməyə davam edə bilərsiniz, lakin bəzi və ya bütün funksiyalar işləməyə və tətbiqin görünüşü yanlış ola bilər.",
"Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> for the best experience.": "Zəhmət olmasa quraşdırın<chromeLink> Chrome</chromeLink> ,<firefoxLink> Firefox</firefoxLink> , və ya<safariLink> Safari</safariLink> ən yaxşı təcrübə üçün.",
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "%(brand)s cari brauzeriniz tərəfindən dəstəklənməyən təkmil brauzer funksiyalarından istifadə edir.",
"Your browser can't run %(brand)s": "Brauzeriniz %(brand)s işlədə bilmir",
"Unsupported browser": "Dəstəklənməyən brauzer",
"Use %(brand)s on mobile": "Mobil telefonda %(brand)s istifadə edin",
"Powered by Matrix": "Gücünü Matrix'dən alır",
"Go to your browser to complete Sign In": "Girişi tamamlamaq üçün brauzerinizə keçin",
"Open": "Aç",
"Download Completed": "Yükləmə Tamamlandı",
"Unable to load config file: please refresh the page to try again.": "Konfiqurasiya faylını yükləmək mümkün deyil: yenidən cəhd etmək üçün səhifəni yeniləyin.",
"Your Element configuration contains invalid JSON. Please correct the problem and reload the page.": "Element konfiqurasiyanızda yanlış JSON var. Problemi düzəldin və səhifəni yenidən yükləyin.",
"Your Element is misconfigured": "Elementi yanlış konfiqurasiya edibsiniz"
}

View File

@ -1 +0,0 @@
{}

View File

@ -1,3 +0,0 @@
{
"Dismiss": "Aдхіліць"
}

View File

@ -1,31 +0,0 @@
{
"Unknown device": "Непознато устройство",
"Dismiss": "Затвори",
"Welcome to Element": "Добре дошли в Element",
"Sign In": "Вписване",
"Create Account": "Създай профил",
"Explore rooms": "Открий стаи",
"Unexpected error preparing the app. See console for details.": "Неочаквана грешка при подготвянето на приложението. Вижте конзолата за подробности.",
"Invalid configuration: no default server specified.": "Невалидна конфигурация: не е указан сървър по подразбиране.",
"The message from the parser is: %(message)s": "Грешката от парсъра е: %(message)s",
"Invalid JSON": "Невалиден JSON",
"Go to your browser to complete Sign In": "Отидете в браузъра за да завършите влизането",
"Unable to load config file: please refresh the page to try again.": "Неуспешно зареждане на конфигурационния файл: презаредете страницата за да опитате пак.",
"Unsupported browser": "Неподдържан браузър",
"Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> for the best experience.": "Инсталирайте <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink> или <safariLink>Safari</safariLink> за най-добра работа.",
"You can continue using your current browser, but some or all features may not work and the look and feel of the application may be incorrect.": "Може да продължите да използвате сегашния си браузър, но някои или всички функции може да се окажат неработещи, или пък външния вид на приложението да изглежда неправилен.",
"I understand the risks and wish to continue": "Разбирам рисковете и желая да продължа",
"Go to element.io": "Отиди на element.io",
"Failed to start": "Неуспешно стартиране",
"Your Element is misconfigured": "Вашият Element не е конфигуриран правилно",
"Your Element configuration contains invalid JSON. Please correct the problem and reload the page.": "Вашата Element конфигурация съдържа невалиден JSON. Коригирайте проблема и презаредете страницата.",
"Download Completed": "Свалянето завърши",
"Open": "Отвори",
"Your browser can't run %(brand)s": "Браузърът ви не може да изпълни %(brand)s",
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "%(brand)s използва модерни функции на браузъра, които не се поддържат от Вашия.",
"Powered by Matrix": "Базирано на Matrix",
"Use %(brand)s on mobile": "Използвайте %(brand)s на мобилен телефон",
"Decentralised, encrypted chat &amp; collaboration powered by $matrixLogo": "Децентрализиран, криптиран чат &amp; сътрудничество, захранено от $matrixlogo",
"%(appName)s: %(browserName)s on %(osName)s": "%(appName)s: %(browserName)s под %(osName)s",
"%(brand)s Desktop: %(platformName)s": "%(brand)s Desktop: %(platformName)s"
}

View File

@ -1,4 +0,0 @@
{
"Dismiss": "সরাও",
"Open": "খোলা"
}

View File

@ -1 +0,0 @@
{}

View File

@ -1,4 +0,0 @@
{
"Your Element is misconfigured": "আপনার এলিমেন্ট টি ভুল ভাবে কনফিগার করা হয়েছে",
"Invalid configuration: no default server specified.": "ভুল কনফিগারেশনঃ কোনো মূল সার্ভার উল্লেখ করা হয়নি।"
}

View File

@ -1,3 +0,0 @@
{
"Open": "Digeriñ"
}

View File

@ -1,28 +0,0 @@
{
"Invalid configuration: no default server specified.": "Neispravna konfiguracija: nije naveden zadani server.",
"Your Element is misconfigured": "Vaš element je pogrešno konfiguriran",
"Your Element configuration contains invalid JSON. Please correct the problem and reload the page.": "Konfiguracija vašeg elementa sadrži nevažeći JSON. Ispravite problem i ponovo učitajte stranicu.",
"The message from the parser is: %(message)s": "Poruka parsera je: %(message)s",
"Invalid JSON": "Nevažeći JSON",
"Unable to load config file: please refresh the page to try again.": "Nije moguće učitati konfiguracijsku datoteku: osvježite stranicu i pokušajte ponovo.",
"Unexpected error preparing the app. See console for details.": "Neočekivana greška prilikom pripreme aplikacije. Pogledajte konzolu za detalje.",
"Download Completed": "Preuzimanje završeno",
"Open": "Otvori",
"Dismiss": "Odbaci",
"Go to your browser to complete Sign In": "Idite na svoj pretraživač da biste dovršili prijavu",
"Unknown device": "Nepoznat uređaj",
"Powered by Matrix": "Pokretano uz Matrix",
"Unsupported browser": "Nepodržani pretraživač",
"Your browser can't run %(brand)s": "Vaš pretraživač ne može pokretati %(brand)s",
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "%(brand)s koristi napredne funkcije pretraživača koje vaš trenutni pretraživač ne podržava.",
"Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> for the best experience.": "Molimo instalirajte <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink> ili <safariLink>Safari</safariLink> za najbolje iskustvo.",
"You can continue using your current browser, but some or all features may not work and the look and feel of the application may be incorrect.": "Možete nastaviti koristiti svoj trenutni pretraživač, ali neke ili sve funkcije možda neće raditi, a izgled i dojam aplikacije mogu biti neispravani.",
"I understand the risks and wish to continue": "Razumijem rizike i želim nastaviti",
"Go to element.io": "Idite na element.io",
"Failed to start": "Pokretanje nije uspjelo",
"Welcome to Element": "Dobrodošli u Element",
"Sign In": "Prijavite se",
"Create Account": "Otvori račun",
"Explore rooms": "Istražite sobe",
"Use %(brand)s on mobile": "Koristi %(brand)s na mobitelu"
}

View File

@ -1,28 +0,0 @@
{
"Dismiss": "Omet",
"Unknown device": "Dispositiu desconegut",
"Welcome to Element": "Benvingut/da a Element",
"Create Account": "Crea un compte",
"Explore rooms": "Explora sales",
"Sign In": "Inicia sessió",
"Invalid configuration: no default server specified.": "Configuració invàlida: no s'ha especificat cap servidor predeterminat.",
"Invalid JSON": "JSON invàlid",
"Go to your browser to complete Sign In": "Vés al navegador per completar l'inici de sessió",
"Your Element is misconfigured": "Element està mal configurat",
"Your Element configuration contains invalid JSON. Please correct the problem and reload the page.": "La configuració d'Element conté un JSON invàlid. Soluciona el problema i actualitza la pàgina.",
"The message from the parser is: %(message)s": "El missatge de l'analitzador és: %(message)s",
"Unable to load config file: please refresh the page to try again.": "No s'ha pogut carregar el fitxer de configuració: actualitza la pàgina per tornar-ho a provar.",
"Unexpected error preparing the app. See console for details.": "Error inesperat durant la preparació de l'aplicació. Consulta la consola pels a més detalls.",
"Download Completed": "Baixada completada",
"Open": "Obre",
"Powered by Matrix": "Amb tecnologia de Matrix",
"Unsupported browser": "Navegador no compatible",
"Your browser can't run %(brand)s": "El teu navegador no pot executar %(brand)s",
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "%(brand)s utilitza funcions del navegador avançades que no són compatibles amb el teu navegador actual.",
"Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> for the best experience.": "Instal·la <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, o <safariLink>Safari</safariLink> per obtenir la millor experiència.",
"You can continue using your current browser, but some or all features may not work and the look and feel of the application may be incorrect.": "Pots continuar utilitzant el teu navegador actual, però algunes o totes les funcions podrien no funcionar i l'aspecte de l'aplicació podria ser incorrecte.",
"I understand the risks and wish to continue": "Entenc els riscos i vull continuar",
"Go to element.io": "Vés a element.io",
"Failed to start": "Ha fallat l'inici",
"Use %(brand)s on mobile": "Utilitza %(brand)s al mòbil"
}

View File

@ -1 +0,0 @@
{}

View File

@ -1,31 +1,40 @@
{
"Welcome to Element": "Vítá vás Element",
"Unknown device": "Neznámé zařízení",
"Dismiss": "Zavřít",
"Sign In": "Přihlásit se",
"Create Account": "Vytvořit účet",
"Explore rooms": "Procházet místnosti",
"The message from the parser is: %(message)s": "Zpráva z parseru je: %(message)s",
"Invalid JSON": "Neplatný JSON",
"Unexpected error preparing the app. See console for details.": "Neočekávaná chyba při přípravě aplikace. Podrobnosti najdete v konzoli.",
"Invalid configuration: no default server specified.": "Neplatná konfigurace: není zadán výchozí server.",
"Go to your browser to complete Sign In": "Přejděte do prohlížeče a dokončete přihlášení",
"Your Element is misconfigured": "Váš Element je nesprávně nastaven",
"Your Element configuration contains invalid JSON. Please correct the problem and reload the page.": "Vaše konfigurace Elementu obsahuje nesprávná data JSON. Vyřešte prosím problém a načtěte znovu stránku.",
"Unable to load config file: please refresh the page to try again.": "Nepodařilo se načíst konfigurační soubor: abyste to zkusili znovu, načtěte prosím znovu stránku.",
"Download Completed": "Stahování dokončeno",
"Open": "Otevřít",
"Unsupported browser": "Nepodporovaný prohlížeč",
"Your browser can't run %(brand)s": "Váš prohlížeč nedokáže spustit %(brand)s",
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "%(brand)s využívá pokročilých funkcí prohlížeče, které ten váš nepodporuje.",
"Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> for the best experience.": "Pro nejlepší zážitek si prosím nainstalujte prohlížeč <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, nebo <safariLink>Safari</safariLink>.",
"You can continue using your current browser, but some or all features may not work and the look and feel of the application may be incorrect.": "Můžete pokračovat v užívání vašeho současného prohlížeče, ale některé (nebo dokonce všechny) funkce nemusí fungovat a vzhled a chování aplikace nemusí být správné.",
"I understand the risks and wish to continue": "Rozumím a přesto chci pokračovat",
"Go to element.io": "Přejít na element.io",
"Failed to start": "Nepovedlo se nastartovat",
"Powered by Matrix": "Běží na Matrixu",
"Use %(brand)s on mobile": "Používání %(brand)s v mobilních zařízeních",
"Decentralised, encrypted chat &amp; collaboration powered by $matrixLogo": "Decentralizovaný, šifrovaný chat a spolupráce na platformě $matrixLogo",
"%(appName)s: %(browserName)s on %(osName)s": "%(appName)s: %(browserName)s na %(osName)s",
"%(brand)s Desktop: %(platformName)s": "%(brand)s Desktop: %(platformName)s"
"action": {
"create_account": "Vytvořit účet",
"dismiss": "Zavřít",
"explore_rooms": "Procházet místnosti",
"open": "Otevřít",
"sign_in": "Přihlásit se"
},
"auth": {
"sso_complete_in_browser_dialog_title": "Přejděte do prohlížeče a dokončete přihlášení"
},
"desktop_default_device_name": "%(brand)s Desktop: %(platformName)s",
"download_completed": "Stahování dokončeno",
"error": {
"app_launch_unexpected_error": "Neočekávaná chyba při přípravě aplikace. Podrobnosti najdete v konzoli.",
"cannot_load_config": "Nepodařilo se načíst konfigurační soubor: abyste to zkusili znovu, načtěte prosím znovu stránku.",
"invalid_configuration_mixed_server": "Neplatná konfigurace: default_hs_url nelze použít spolu s default_server_name nebo default_server_config",
"invalid_configuration_no_server": "Neplatná konfigurace: není zadán výchozí server.",
"invalid_json": "Vaše konfigurace Elementu obsahuje nesprávná data JSON. Vyřešte prosím problém a načtěte znovu stránku.",
"invalid_json_detail": "Zpráva z parseru je: %(message)s",
"invalid_json_generic": "Neplatný JSON",
"misconfigured": "Váš Element je nesprávně nastaven"
},
"failed_to_start": "Nepovedlo se nastartovat",
"go_to_element_io": "Přejít na element.io",
"incompatible_browser": {
"browser_links": "Pro nejlepší zážitek si prosím nainstalujte prohlížeč <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, nebo <safariLink>Safari</safariLink>.",
"continue_warning": "Rozumím a přesto chci pokračovat",
"feature_warning": "Můžete pokračovat v užívání vašeho současného prohlížeče, ale některé (nebo dokonce všechny) funkce nemusí fungovat a vzhled a chování aplikace nemusí být správné.",
"features": "%(brand)s využívá pokročilých funkcí prohlížeče, které ten váš nepodporuje.",
"summary": "Váš prohlížeč nedokáže spustit %(brand)s",
"title": "Nepodporovaný prohlížeč"
},
"powered_by_matrix": "Běží na Matrixu",
"powered_by_matrix_with_logo": "Decentralizovaný, šifrovaný chat a spolupráce na platformě $matrixLogo",
"unknown_device": "Neznámé zařízení",
"use_brand_on_mobile": "Používání %(brand)s v mobilních zařízeních",
"web_default_device_name": "%(appName)s: %(browserName)s na %(osName)s",
"welcome_to_element": "Vítá vás Element"
}

View File

@ -1,13 +0,0 @@
{
"The message from the parser is: %(message)s": "Y neges gan y dosrannudd yn: %(message)s",
"Invalid JSON": "JSON annilys",
"Unexpected error preparing the app. See console for details.": "Gwall annisgwyl wrth baratoi'r app. Gweler y consol am fanylion.",
"Invalid configuration: no default server specified.": "Gosodiad annilys: ni nodwyd gweinydd diofyn.",
"Unknown device": "Dyfais anhysbys",
"Dismiss": "Wfftio",
"Welcome to Element": "Croeso i Element",
"Sign In": "Mewngofnodi",
"Create Account": "Creu Cyfrif",
"Explore rooms": "Archwilio Ystafelloedd",
"Go to your browser to complete Sign In": "Ewch i'ch porwr i gwblhau Mewngofnodi"
}

View File

@ -1,31 +0,0 @@
{
"Dismiss": "Afvis",
"Unknown device": "Ukendt enhed",
"Welcome to Element": "Velkommen til Element",
"The message from the parser is: %(message)s": "Beskeden fra parseren er: %(message)s",
"Invalid JSON": "Ugyldig JSON",
"Unexpected error preparing the app. See console for details.": "Uventet fejl ved forberedelse af appen. Se konsollen for detaljer.",
"Invalid configuration: no default server specified.": "Ugyldig konfiguration: Ingen standardserver er angivet.",
"Sign In": "Log ind",
"Create Account": "Opret brugerkonto",
"Explore rooms": "Udforsk rum",
"Unable to load config file: please refresh the page to try again.": "Ikke i stand til at indlæse konfigurationsfil: Genopfrisk venligst siden for at prøve igen.",
"Go to your browser to complete Sign In": "Gå til din browser for at færdiggøre Log ind",
"Go to element.io": "Gå til element.io",
"I understand the risks and wish to continue": "Jeg forstår risikoen og ønsker at fortsætte",
"Unsupported browser": "Browser ikke understøttet",
"Open": "Åbn",
"Download Completed": "Hentning færdig",
"Your Element configuration contains invalid JSON. Please correct the problem and reload the page.": "Din Element konfiguration indeholder ugyldig JSON. Løs venligst problemet og genindlæs siden.",
"Your Element is misconfigured": "Dit Element er konfigureret forkert",
"Your browser can't run %(brand)s": "Din browser kan ikke køre %(brand)s",
"Powered by Matrix": "Drevet af Matrix",
"Failed to start": "Opstart mislykkedes",
"You can continue using your current browser, but some or all features may not work and the look and feel of the application may be incorrect.": "Du kan fortsætte med at bruge din nuværende browser, men du kan opleve at visse eller alle funktioner ikke vil fungere korrekt.",
"Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> for the best experience.": "Venligst installer <chromeLink>Chrome</chromeLink>,<firefoxLink>Firefox</firefoxLink> eller <safariLink>Safari</safariLink> for den bedste oplevelse.",
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "%(brand)s bruger avanceret browser funktioner som ikke er understøttet af din nuværende browser.",
"Use %(brand)s on mobile": "Brug %(brand)s på mobil",
"Decentralised, encrypted chat &amp; collaboration powered by $matrixLogo": "Decentraliseret, krypteret chat &amp; samarbejde drevet af $matrixLogo",
"%(appName)s: %(browserName)s on %(osName)s": "%(appName)s: %(browserName)s på %(osName)s",
"%(brand)s Desktop: %(platformName)s": "%(brand)s Desktop: %(platformName)s"
}

View File

@ -1,32 +1,39 @@
{
"Dismiss": "Ausblenden",
"Unknown device": "Unbekanntes Gerät",
"Welcome to Element": "Willkommen bei Element",
"Sign In": "Anmelden",
"Create Account": "Konto erstellen",
"Explore rooms": "Räume erkunden",
"Unexpected error preparing the app. See console for details.": "Unerwarteter Fehler bei der Vorbereitung der App; mehr Details in der Konsole.",
"Invalid configuration: no default server specified.": "Ungültige Konfiguration: Es wurde kein Standardserver angegeben.",
"The message from the parser is: %(message)s": "Die Nachricht des Parsers ist: %(message)s",
"Invalid JSON": "Ungültiges JSON",
"Go to your browser to complete Sign In": "Gehe zu deinem Browser, um die Anmeldung abzuschließen",
"Unable to load config file: please refresh the page to try again.": "Konfigurationsdatei kann nicht geladen werden: Bitte aktualisiere die Seite, um es erneut zu versuchen.",
"Unsupported browser": "Nicht unterstützter Browser",
"Go to element.io": "Gehe zu element.io",
"Failed to start": "Start fehlgeschlagen",
"Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> for the best experience.": "Bitte installiere <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink> oder <safariLink>Safari</safariLink> für das beste Erlebnis.",
"You can continue using your current browser, but some or all features may not work and the look and feel of the application may be incorrect.": "Du kannst deinen aktuellen Browser weiterhin verwenden. Es ist aber möglich, dass nicht alles richtig funktioniert oder das Aussehen der App inkorrekt ist.",
"I understand the risks and wish to continue": "Ich verstehe die Risiken und möchte fortfahren",
"Your Element is misconfigured": "Dein Element ist falsch konfiguriert",
"Your Element configuration contains invalid JSON. Please correct the problem and reload the page.": "Deine Element-Konfiguration enthält ungültiges JSON. Bitte korrigiere das Problem und lade die Seite neu.",
"Download Completed": "Herunterladen fertiggestellt",
"Open": "Öffnen",
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "%(brand)s verwendet erweiterte Browserfunktionen, die von deinem Browser nicht unterstützt werden.",
"Your browser can't run %(brand)s": "Dein Browser kann %(brand)s nicht ausführen",
"Powered by Matrix": "Betrieben mit Matrix",
"Use %(brand)s on mobile": "Verwende %(brand)s am Handy",
"Decentralised, encrypted chat &amp; collaboration powered by $matrixLogo": "Dezentralisierter, verschlüsselter Chat &amp; Zusammenarbeit unterstützt von $matrixLogo",
"%(appName)s: %(browserName)s on %(osName)s": "%(appName)s: %(browserName)s auf %(osName)s",
"%(brand)s Desktop: %(platformName)s": "%(brand)s Desktop: %(platformName)s",
"Invalid configuration: a default_hs_url can't be specified along with default_server_name or default_server_config": "Ungültige Konfiguration: default_hs_url kann nicht zeitgleich mit default_server_name oder default_server_config festgelegt werden"
"action": {
"create_account": "Konto erstellen",
"dismiss": "Ausblenden",
"explore_rooms": "Räume erkunden",
"open": "Öffnen",
"sign_in": "Anmelden"
},
"auth": {
"sso_complete_in_browser_dialog_title": "Browser öffnen, um die Anmeldung abzuschließen"
},
"download_completed": "Herunterladen fertiggestellt",
"error": {
"app_launch_unexpected_error": "Unerwarteter Fehler bei der Vorbereitung der App; mehr Details in der Konsole.",
"cannot_load_config": "Konfigurationsdatei kann nicht geladen werden: Bitte aktualisiere die Seite, um es erneut zu versuchen.",
"invalid_configuration_mixed_server": "Ungültige Konfiguration: default_hs_url kann nicht zeitgleich mit default_server_name oder default_server_config festgelegt werden",
"invalid_configuration_no_server": "Ungültige Konfiguration: Es wurde kein Standardserver angegeben.",
"invalid_json": "Deine Element-Konfiguration enthält ungültiges JSON. Bitte korrigiere das Problem und lade die Seite neu.",
"invalid_json_detail": "Die Nachricht des Parsers ist: %(message)s",
"invalid_json_generic": "Ungültiges JSON",
"misconfigured": "Dein Element ist falsch konfiguriert"
},
"failed_to_start": "Start fehlgeschlagen",
"go_to_element_io": "Gehe zu element.io",
"incompatible_browser": {
"browser_links": "Bitte installiere <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink> oder <safariLink>Safari</safariLink> für das beste Erlebnis.",
"continue_warning": "Ich verstehe die Risiken und möchte fortfahren",
"feature_warning": "Du kannst deinen aktuellen Browser weiterhin verwenden. Es ist aber möglich, dass nicht alles richtig funktioniert oder das Aussehen der App inkorrekt ist.",
"features": "%(brand)s verwendet erweiterte Browserfunktionen, die von deinem Browser nicht unterstützt werden.",
"summary": "Dein Browser kann %(brand)s nicht ausführen",
"title": "Nicht unterstützter Browser"
},
"powered_by_matrix": "Betrieben mit Matrix",
"powered_by_matrix_with_logo": "Dezentralisierter, verschlüsselter Chat &amp; Zusammenarbeit unterstützt von $matrixLogo",
"unknown_device": "Unbekanntes Gerät",
"use_brand_on_mobile": "Verwende %(brand)s am Handy",
"web_default_device_name": "%(appName)s: %(browserName)s auf %(osName)s",
"welcome_to_element": "Willkommen bei Element"
}

View File

@ -1,29 +1,37 @@
{
"Dismiss": "Απόρριψη",
"Unknown device": "Άγνωστη συσκευή",
"Welcome to Element": "Καλώς ήλθατε στο Element",
"Sign In": "Σύνδεση",
"Create Account": "Δημιουργία Λογαριασμού",
"The message from the parser is: %(message)s": "Το μήνυμα από τον αναλυτή είναι: %(message)s",
"Invalid JSON": "Μη έγκυρο JSON",
"Unexpected error preparing the app. See console for details.": "Απρόοπτο σφάλμα κατά την προετοιμασία της εφαρμογής. Δείτε το τερματικό για λεπτομέρειες.",
"Invalid configuration: no default server specified.": "Μη έγκυρη ρύθμιση παραμέτρων: δεν έχει οριστεί προκαθορισμένος διακομιστής.",
"Explore rooms": "Εξερευνήστε δωμάτια",
"Open": "Άνοιγμα",
"Go to your browser to complete Sign In": "Μεταβείτε στο πρόγραμμα περιήγησής σας για να ολοκληρώσετε τη σύνδεση",
"Powered by Matrix": "Με την υποστήριξη του Matrix",
"Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> for the best experience.": "Παρακαλούμε εγκαταστήστε <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, ή <safariLink>Safari</safariLink> για καλύτερη εμπειρία χρήσης.",
"Your Element is misconfigured": "Το Element σας δεν εχει ρυθμιστεί σωστά",
"Your Element configuration contains invalid JSON. Please correct the problem and reload the page.": "Η ρύθμιση του Element περιέχει μη έγκυρο JSON. Διορθώστε το πρόβλημα και φορτώστε ξανά τη σελίδα.",
"Unable to load config file: please refresh the page to try again.": "Δεν είναι δυνατή η φόρτωση του αρχείου config: ανανεώστε τη σελίδα για να δοκιμάσετε ξανά.",
"Download Completed": "Η λήψη ολοκληρώθηκε",
"Unsupported browser": "Μη υποστηριζόμενο πρόγραμμα περιήγησης",
"Your browser can't run %(brand)s": "Το πρόγραμμα περιήγησής σας δεν μπορεί να εκτελέσει %(brand)s",
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "%(brand)s χρησιμοποιεί προηγμένες δυνατότητες προγράμματος περιήγησης που δεν υποστηρίζονται από το τρέχον πρόγραμμα περιήγησής σας.",
"You can continue using your current browser, but some or all features may not work and the look and feel of the application may be incorrect.": "Μπορείτε να συνεχίσετε να χρησιμοποιείτε το τρέχον πρόγραμμα περιήγησής σας, αλλά ορισμένες ή όλες οι λειτουργίες ενδέχεται να μην λειτουργούν και η εμφάνιση και η αίσθηση της εφαρμογής ενδέχεται να είναι λανθασμένη.",
"I understand the risks and wish to continue": "Κατανοώ τους κινδύνους και επιθυμώ να συνεχίσω",
"Go to element.io": "Πήγαινε στο element.io",
"Failed to start": "Αποτυχία έναρξης",
"Use %(brand)s on mobile": "Χρήση %(brand)s σε κινητό",
"Decentralised, encrypted chat &amp; collaboration powered by $matrixLogo": "Αποκεντρωμένη, κρυπτογραφημένη συνομιλία και συνεργασία χρησιμοποιώντας το $matrixLogo"
"action": {
"create_account": "Δημιουργία Λογαριασμού",
"dismiss": "Απόρριψη",
"explore_rooms": "Εξερευνήστε δωμάτια",
"open": "Άνοιγμα",
"sign_in": "Σύνδεση"
},
"auth": {
"sso_complete_in_browser_dialog_title": "Μεταβείτε στο πρόγραμμα περιήγησής σας για να ολοκληρώσετε τη σύνδεση"
},
"download_completed": "Η λήψη ολοκληρώθηκε",
"error": {
"app_launch_unexpected_error": "Απρόοπτο σφάλμα κατά την προετοιμασία της εφαρμογής. Δείτε το τερματικό για λεπτομέρειες.",
"cannot_load_config": "Δεν είναι δυνατή η φόρτωση του αρχείου config: ανανεώστε τη σελίδα για να δοκιμάσετε ξανά.",
"invalid_configuration_no_server": "Μη έγκυρη ρύθμιση παραμέτρων: δεν έχει οριστεί προκαθορισμένος διακομιστής.",
"invalid_json": "Η ρύθμιση του Element περιέχει μη έγκυρο JSON. Διορθώστε το πρόβλημα και φορτώστε ξανά τη σελίδα.",
"invalid_json_detail": "Το μήνυμα από τον αναλυτή είναι: %(message)s",
"invalid_json_generic": "Μη έγκυρο JSON",
"misconfigured": "Το Element σας δεν εχει ρυθμιστεί σωστά"
},
"failed_to_start": "Αποτυχία έναρξης",
"go_to_element_io": "Πήγαινε στο element.io",
"incompatible_browser": {
"browser_links": "Παρακαλούμε εγκαταστήστε <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, ή <safariLink>Safari</safariLink> για καλύτερη εμπειρία χρήσης.",
"continue_warning": "Κατανοώ τους κινδύνους και επιθυμώ να συνεχίσω",
"feature_warning": "Μπορείτε να συνεχίσετε να χρησιμοποιείτε το τρέχον πρόγραμμα περιήγησής σας, αλλά ορισμένες ή όλες οι λειτουργίες ενδέχεται να μην λειτουργούν και η εμφάνιση και η αίσθηση της εφαρμογής ενδέχεται να είναι λανθασμένη.",
"features": "%(brand)s χρησιμοποιεί προηγμένες δυνατότητες προγράμματος περιήγησης που δεν υποστηρίζονται από το τρέχον πρόγραμμα περιήγησής σας.",
"summary": "Το πρόγραμμα περιήγησής σας δεν μπορεί να εκτελέσει %(brand)s",
"title": "Μη υποστηριζόμενο πρόγραμμα περιήγησης"
},
"powered_by_matrix": "Με την υποστήριξη του Matrix",
"powered_by_matrix_with_logo": "Αποκεντρωμένη, κρυπτογραφημένη συνομιλία και συνεργασία χρησιμοποιώντας το $matrixLogo",
"unknown_device": "Άγνωστη συσκευή",
"use_brand_on_mobile": "Χρήση %(brand)s σε κινητό",
"welcome_to_element": "Καλώς ήλθατε στο Element"
}

View File

@ -1,32 +1,40 @@
{
"Invalid configuration: a default_hs_url can't be specified along with default_server_name or default_server_config": "Invalid configuration: a default_hs_url can't be specified along with default_server_name or default_server_config",
"Invalid configuration: no default server specified.": "Invalid configuration: no default server specified.",
"Your Element is misconfigured": "Your Element is misconfigured",
"Your Element configuration contains invalid JSON. Please correct the problem and reload the page.": "Your Element configuration contains invalid JSON. Please correct the problem and reload the page.",
"The message from the parser is: %(message)s": "The message from the parser is: %(message)s",
"Invalid JSON": "Invalid JSON",
"Unable to load config file: please refresh the page to try again.": "Unable to load config file: please refresh the page to try again.",
"Unexpected error preparing the app. See console for details.": "Unexpected error preparing the app. See console for details.",
"Download Completed": "Download Completed",
"Open": "Open",
"Dismiss": "Dismiss",
"%(brand)s Desktop: %(platformName)s": "%(brand)s Desktop: %(platformName)s",
"Go to your browser to complete Sign In": "Go to your browser to complete Sign In",
"Unknown device": "Unknown device",
"%(appName)s: %(browserName)s on %(osName)s": "%(appName)s: %(browserName)s on %(osName)s",
"Powered by Matrix": "Powered by Matrix",
"Use %(brand)s on mobile": "Use %(brand)s on mobile",
"Unsupported browser": "Unsupported browser",
"Your browser can't run %(brand)s": "Your browser can't run %(brand)s",
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "%(brand)s uses advanced browser features which aren't supported by your current browser.",
"Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> for the best experience.": "Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> for the best experience.",
"You can continue using your current browser, but some or all features may not work and the look and feel of the application may be incorrect.": "You can continue using your current browser, but some or all features may not work and the look and feel of the application may be incorrect.",
"I understand the risks and wish to continue": "I understand the risks and wish to continue",
"Go to element.io": "Go to element.io",
"Failed to start": "Failed to start",
"Welcome to Element": "Welcome to Element",
"Decentralised, encrypted chat &amp; collaboration powered by $matrixLogo": "Decentralised, encrypted chat &amp; collaboration powered by $matrixLogo",
"Sign In": "Sign In",
"Create Account": "Create Account",
"Explore rooms": "Explore rooms"
"action": {
"create_account": "Create Account",
"dismiss": "Dismiss",
"explore_rooms": "Explore rooms",
"open": "Open",
"sign_in": "Sign in"
},
"auth": {
"sso_complete_in_browser_dialog_title": "Go to your browser to complete Sign In"
},
"desktop_default_device_name": "%(brand)s Desktop: %(platformName)s",
"download_completed": "Download Completed",
"error": {
"app_launch_unexpected_error": "Unexpected error preparing the app. See console for details.",
"cannot_load_config": "Unable to load config file: please refresh the page to try again.",
"invalid_configuration_mixed_server": "Invalid configuration: a default_hs_url can't be specified along with default_server_name or default_server_config",
"invalid_configuration_no_server": "Invalid configuration: no default server specified.",
"invalid_json": "Your Element configuration contains invalid JSON. Please correct the problem and reload the page.",
"invalid_json_detail": "The message from the parser is: %(message)s",
"invalid_json_generic": "Invalid JSON",
"misconfigured": "Your Element is misconfigured"
},
"failed_to_start": "Failed to start",
"go_to_element_io": "Go to element.io",
"incompatible_browser": {
"browser_links": "Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> for the best experience.",
"continue_warning": "I understand the risks and wish to continue",
"feature_warning": "You can continue using your current browser, but some or all features may not work and the look and feel of the application may be incorrect.",
"features": "%(brand)s uses advanced browser features which aren't supported by your current browser.",
"summary": "Your browser can't run %(brand)s",
"title": "Unsupported browser"
},
"powered_by_matrix": "Powered by Matrix",
"powered_by_matrix_with_logo": "Decentralised, encrypted chat &amp; collaboration powered by $matrixLogo",
"unknown_device": "Unknown device",
"use_brand_on_mobile": "Use %(brand)s on mobile",
"web_default_device_name": "%(appName)s: %(browserName)s on %(osName)s",
"welcome_to_element": "Welcome to Element"
}

View File

@ -1,31 +1,5 @@
{
"Dismiss": "Dismiss",
"Unknown device": "Unknown device",
"Welcome to Element": "Welcome to Element",
"Sign In": "Sign In",
"Create Account": "Create Account",
"Explore rooms": "Explore rooms",
"The message from the parser is: %(message)s": "The message from the parser is: %(message)s",
"Invalid JSON": "Invalid JSON",
"Unexpected error preparing the app. See console for details.": "Unexpected error preparing the app. See console for details.",
"Invalid configuration: no default server specified.": "Invalid configuration: no default server specified.",
"Failed to start": "Failed to start",
"Go to element.io": "Go to element.io",
"I understand the risks and wish to continue": "I understand the risks and wish to continue",
"You can continue using your current browser, but some or all features may not work and the look and feel of the application may be incorrect.": "You can continue using your current browser, but some or all features may not work and the look and feel of the application may be incorrect.",
"Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> for the best experience.": "Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> for the best experience.",
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "%(brand)s uses advanced browser features which aren't supported by your current browser.",
"Your browser can't run %(brand)s": "Your browser can't run %(brand)s",
"Unsupported browser": "Unsupported browser",
"Powered by Matrix": "Powered by Matrix",
"Go to your browser to complete Sign In": "Go to your browser to complete Sign In",
"Open": "Open",
"Download Completed": "Download Completed",
"Unable to load config file: please refresh the page to try again.": "Unable to load config file: please refresh the page to try again.",
"Your Element configuration contains invalid JSON. Please correct the problem and reload the page.": "Your Element configuration contains invalid JSON. Please correct the problem and reload the page.",
"Your Element is misconfigured": "Your Element is misconfigured",
"Decentralised, encrypted chat &amp; collaboration powered by $matrixLogo": "Decentralised, encrypted chat &amp; collaboration powered by $matrixLogo",
"Use %(brand)s on mobile": "Use %(brand)s on mobile",
"%(appName)s: %(browserName)s on %(osName)s": "%(appName)s: %(browserName)s on %(osName)s",
"%(brand)s Desktop: %(platformName)s": "%(brand)s Desktop: %(platformName)s"
"action": {
"sign_in": "Sign in"
}
}

View File

@ -1,31 +1,39 @@
{
"Dismiss": "Rezigni",
"Unknown device": "Nekonata aparato",
"Welcome to Element": "Bonvenon al Element",
"Sign In": "Ensaluti",
"Create Account": "Krei konton",
"Explore rooms": "Esplori ĉambrojn",
"Unexpected error preparing the app. See console for details.": "Neatendita eraro okazis dum la preparado de la aplikaĵo. Rigardu la konzolon por detaloj.",
"Invalid configuration: no default server specified.": "Nevalida agordo: neniu implicita servilo estas specifita.",
"The message from the parser is: %(message)s": "La mesaĝo el la analizilo estas: %(message)s",
"Invalid JSON": "Nevalida JSON",
"Go to your browser to complete Sign In": "Iru al via retumilo por finpretigi la ensaluton",
"Unable to load config file: please refresh the page to try again.": "Ne povas enlegi agordan dosieron: bonvolu reprovi per aktualigo de la paĝo.",
"Unsupported browser": "Nesubtenata retumilo",
"Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> for the best experience.": "Bonvolu instali retumilon <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, aŭ <safariLink>Safari</safariLink>, por la plej bona sperto.",
"You can continue using your current browser, but some or all features may not work and the look and feel of the application may be incorrect.": "Vi povas daŭre uzi vian nunan foliumilon, sed iuj (eĉ ĉiuj) funkcioj eble ne funkciu, kaj la aspekto de la aplikaĵo eble ne estu ĝusta.",
"I understand the risks and wish to continue": "Mi komprenas la riskon kaj volas pluiĝi",
"Go to element.io": "Iri al element.io",
"Failed to start": "Malsukcesis starti",
"Download Completed": "Elŝuto finiĝis",
"Open": "Malfermi",
"Your Element is misconfigured": "Via Elemento estas misagordita",
"Your Element configuration contains invalid JSON. Please correct the problem and reload the page.": "Via agordaro de Elemento enhavas nevalidajn datumojn de JSON. Bonvolu korekti la problemon kaj aktualigi la paĝon.",
"Your browser can't run %(brand)s": "Via retumilo ne povas ruli %(brand)s",
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "%(brand)s uzas specialajn funkciojn de retumilo, kiujn via nuna retumilo ne subtenas.",
"Powered by Matrix": "Povigata de Matrix",
"Use %(brand)s on mobile": "Uzi %(brand)s poŝtelefone",
"Decentralised, encrypted chat &amp; collaboration powered by $matrixLogo": "Malcentralizita kaj ĉifrita babilejo; kunlaboro danke al $matrixLogo",
"%(appName)s: %(browserName)s on %(osName)s": "%(appName)s: %(browserName)s sur %(osName)s",
"%(brand)s Desktop: %(platformName)s": "%(brand)s Labortablo: %(platformName)s"
"action": {
"create_account": "Krei konton",
"dismiss": "Rezigni",
"explore_rooms": "Esplori ĉambrojn",
"open": "Malfermi",
"sign_in": "Ensaluti"
},
"auth": {
"sso_complete_in_browser_dialog_title": "Iru al via retumilo por finpretigi la ensaluton"
},
"desktop_default_device_name": "%(brand)s Labortablo: %(platformName)s",
"download_completed": "Elŝuto finiĝis",
"error": {
"app_launch_unexpected_error": "Neatendita eraro okazis dum la preparado de la aplikaĵo. Rigardu la konzolon por detaloj.",
"cannot_load_config": "Ne povas enlegi agordan dosieron: bonvolu reprovi per aktualigo de la paĝo.",
"invalid_configuration_no_server": "Nevalida agordo: neniu implicita servilo estas specifita.",
"invalid_json": "Via agordaro de Elemento enhavas nevalidajn datumojn de JSON. Bonvolu korekti la problemon kaj aktualigi la paĝon.",
"invalid_json_detail": "La mesaĝo el la analizilo estas: %(message)s",
"invalid_json_generic": "Nevalida JSON",
"misconfigured": "Via Elemento estas misagordita"
},
"failed_to_start": "Malsukcesis starti",
"go_to_element_io": "Iri al element.io",
"incompatible_browser": {
"browser_links": "Bonvolu instali retumilon <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, aŭ <safariLink>Safari</safariLink>, por la plej bona sperto.",
"continue_warning": "Mi komprenas la riskon kaj volas pluiĝi",
"feature_warning": "Vi povas daŭre uzi vian nunan foliumilon, sed iuj (eĉ ĉiuj) funkcioj eble ne funkciu, kaj la aspekto de la aplikaĵo eble ne estu ĝusta.",
"features": "%(brand)s uzas specialajn funkciojn de retumilo, kiujn via nuna retumilo ne subtenas.",
"summary": "Via retumilo ne povas ruli %(brand)s",
"title": "Nesubtenata retumilo"
},
"powered_by_matrix": "Povigata de Matrix",
"powered_by_matrix_with_logo": "Malcentralizita kaj ĉifrita babilejo; kunlaboro danke al $matrixLogo",
"unknown_device": "Nekonata aparato",
"use_brand_on_mobile": "Uzi %(brand)s poŝtelefone",
"web_default_device_name": "%(appName)s: %(browserName)s sur %(osName)s",
"welcome_to_element": "Bonvenon al Element"
}

View File

@ -1,31 +1,39 @@
{
"Unknown device": "Dispositivo desconocido",
"Dismiss": "Omitir",
"Welcome to Element": "Te damos la bienvenida a Element",
"Sign In": "Iniciar sesión",
"Create Account": "Crear cuenta",
"Explore rooms": "Explorar salas",
"Unexpected error preparing the app. See console for details.": "Error inesperado preparando la aplicación. Ver la consola para más detalles.",
"Invalid configuration: no default server specified.": "Configuración errónea: no se ha especificado servidor.",
"The message from the parser is: %(message)s": "El mensaje del parser es: %(message)s",
"Invalid JSON": "JSON inválido",
"Go to your browser to complete Sign In": "Abre tu navegador web para completar el registro",
"Unable to load config file: please refresh the page to try again.": "No se ha podido cargar el archivo de configuración. Recarga la página para intentarlo otra vez.",
"Unsupported browser": "Navegador no compatible",
"Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> for the best experience.": "Por favor, instale <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, o <safariLink>Safari</safariLink> para la mejor experiencia.",
"You can continue using your current browser, but some or all features may not work and the look and feel of the application may be incorrect.": "Puedes seguir utilizando tu navegador actual, pero puede que algunas funcionalidades no estén disponibles o que algunas partes de la aplicación se muestren de forma incorrecta.",
"I understand the risks and wish to continue": "Entiendo los riesgos y quiero continuar",
"Go to element.io": "Ir a element.io",
"Failed to start": "Fallo al iniciar",
"Your Element is misconfigured": "Tu aplicación Element está mal configurada",
"Your Element configuration contains invalid JSON. Please correct the problem and reload the page.": "Tu configuración de Element contiene JSON inválido. Por favor corrígelo e inténtelo de nuevo.",
"Download Completed": "Descarga completada",
"Open": "Abrir",
"Your browser can't run %(brand)s": "Tu navegador no es compatible con %(brand)s",
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "%(brand)s usa funciones avanzadas que su navegador actual no soporta.",
"Powered by Matrix": "Funciona con Matrix",
"Use %(brand)s on mobile": "Usar %(brand)s en modo móvil",
"Decentralised, encrypted chat &amp; collaboration powered by $matrixLogo": "Conversaciones y colaboración descentralizadas y cifradas gracias a $matrixLogo",
"%(appName)s: %(browserName)s on %(osName)s": "%(appName)s: %(browserName)s en %(osName)s",
"%(brand)s Desktop: %(platformName)s": "%(brand)s de escritorio: %(platformName)s"
"action": {
"create_account": "Crear cuenta",
"dismiss": "Omitir",
"explore_rooms": "Explorar salas",
"open": "Abrir",
"sign_in": "Iniciar sesión"
},
"auth": {
"sso_complete_in_browser_dialog_title": "Abre tu navegador web para completar el registro"
},
"desktop_default_device_name": "%(brand)s de escritorio: %(platformName)s",
"download_completed": "Descarga completada",
"error": {
"app_launch_unexpected_error": "Error inesperado preparando la aplicación. Ver la consola para más detalles.",
"cannot_load_config": "No se ha podido cargar el archivo de configuración. Recarga la página para intentarlo otra vez.",
"invalid_configuration_no_server": "Configuración errónea: no se ha especificado servidor.",
"invalid_json": "Tu configuración de Element contiene JSON inválido. Por favor corrígelo e inténtelo de nuevo.",
"invalid_json_detail": "El mensaje del parser es: %(message)s",
"invalid_json_generic": "JSON inválido",
"misconfigured": "Tu aplicación Element está mal configurada"
},
"failed_to_start": "Fallo al iniciar",
"go_to_element_io": "Ir a element.io",
"incompatible_browser": {
"browser_links": "Por favor, instale <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, o <safariLink>Safari</safariLink> para la mejor experiencia.",
"continue_warning": "Entiendo los riesgos y quiero continuar",
"feature_warning": "Puedes seguir utilizando tu navegador actual, pero puede que algunas funcionalidades no estén disponibles o que algunas partes de la aplicación se muestren de forma incorrecta.",
"features": "%(brand)s usa funciones avanzadas que su navegador actual no soporta.",
"summary": "Tu navegador no es compatible con %(brand)s",
"title": "Navegador no compatible"
},
"powered_by_matrix": "Funciona con Matrix",
"powered_by_matrix_with_logo": "Conversaciones y colaboración descentralizadas y cifradas gracias a $matrixLogo",
"unknown_device": "Dispositivo desconocido",
"use_brand_on_mobile": "Usar %(brand)s en modo móvil",
"web_default_device_name": "%(appName)s: %(browserName)s en %(osName)s",
"welcome_to_element": "Te damos la bienvenida a Element"
}

View File

@ -1,32 +1,40 @@
{
"The message from the parser is: %(message)s": "Sõnum parserist on: %(message)s",
"Invalid JSON": "Vigane JSON",
"Unknown device": "Tundmatu seade",
"Invalid configuration: no default server specified.": "Vigane seadistus: vaikimisi server on määramata.",
"Unable to load config file: please refresh the page to try again.": "Seadistuste faili laadimine ei õnnestunud: uuesti proovimiseks palun laadi leht uuesti.",
"Unexpected error preparing the app. See console for details.": "Rakenduse ettevalmistamisel tekkis ootamatu viga. Täpsema teabe leiad konsoolist.",
"Go to your browser to complete Sign In": "Sisselogimiseks ava oma brauser",
"Dismiss": "Loobu",
"Explore rooms": "Tutvu jututubadega",
"Welcome to Element": "Tere tulemast kasutama suhtlusrakendust Element",
"Sign In": "Logi sisse",
"Create Account": "Loo konto",
"Unsupported browser": "Sellele brauserile puudub tugi",
"Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> for the best experience.": "Parima kasutuskogemuse jaoks palun paigalda <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink> või <safariLink>Safari</safariLink>.",
"You can continue using your current browser, but some or all features may not work and the look and feel of the application may be incorrect.": "Sa võid jätkata praeguse brauseri kasutamist, kuid mõned või kõik funktsionaalsused ei pruugi toimida ning rakenduse välimus võib vigane olla.",
"I understand the risks and wish to continue": "Ma mõistan riske ja soovin jätkata",
"Go to element.io": "Mine element.io lehele",
"Failed to start": "Käivitamine ei õnnestunud",
"Download Completed": "Allalaadimine on lõpetatud",
"Open": "Ava",
"Your Element is misconfigured": "Sinu Element on valesti seadistatud",
"Your Element configuration contains invalid JSON. Please correct the problem and reload the page.": "Sinu Element'i seadistustes on vigased JSON-vormingus andmed. Palun paranda see viga ja laadi leht uuesti.",
"Your browser can't run %(brand)s": "%(brand)s ei toimi sinu brauseris",
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "%(brand)s kasutab mitmeid uusi brauseri-põhiseid tehnoloogiaid, mis ei ole veel sinu veebibrauseris toetatud.",
"Powered by Matrix": "Põhineb Matrix'il",
"Use %(brand)s on mobile": "Kasuta rakendust %(brand)s nutiseadmes",
"Decentralised, encrypted chat &amp; collaboration powered by $matrixLogo": "Hajutatud ja krüpteeritud suhtlus- ning ühistöörakendus, mille aluseks on $matrixLogo",
"%(appName)s: %(browserName)s on %(osName)s": "%(appName)s: %(browserName)s operatsioonisüsteemis %(osName)s",
"%(brand)s Desktop: %(platformName)s": "%(brand)s Desktop: %(platformName)s",
"Invalid configuration: a default_hs_url can't be specified along with default_server_name or default_server_config": "Vigane seadistus: default_hs_url ei saa olla määratud koos default_server_name või default_server_config tunnustega"
"action": {
"create_account": "Loo konto",
"dismiss": "Loobu",
"explore_rooms": "Tutvu jututubadega",
"open": "Ava",
"sign_in": "Logi sisse"
},
"auth": {
"sso_complete_in_browser_dialog_title": "Sisselogimiseks ava oma brauser"
},
"desktop_default_device_name": "%(brand)s töölaud: %(platformName)s",
"download_completed": "Allalaadimine on lõpetatud",
"error": {
"app_launch_unexpected_error": "Rakenduse ettevalmistamisel tekkis ootamatu viga. Täpsema teabe leiad konsoolist.",
"cannot_load_config": "Seadistuste faili laadimine ei õnnestunud: uuesti proovimiseks palun laadi leht uuesti.",
"invalid_configuration_mixed_server": "Vigane seadistus: default_hs_url ei saa olla määratud koos default_server_name või default_server_config tunnustega",
"invalid_configuration_no_server": "Vigane seadistus: vaikimisi server on määramata.",
"invalid_json": "Sinu Element'i seadistustes on vigased JSON-vormingus andmed. Palun paranda see viga ja laadi leht uuesti.",
"invalid_json_detail": "Sõnum parserist on: %(message)s",
"invalid_json_generic": "Vigane JSON",
"misconfigured": "Sinu Element on valesti seadistatud"
},
"failed_to_start": "Käivitamine ei õnnestunud",
"go_to_element_io": "Mine element.io lehele",
"incompatible_browser": {
"browser_links": "Parima kasutuskogemuse jaoks palun paigalda <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink> või <safariLink>Safari</safariLink>.",
"continue_warning": "Ma mõistan riske ja soovin jätkata",
"feature_warning": "Sa võid jätkata praeguse brauseri kasutamist, kuid mõned või kõik funktsionaalsused ei pruugi toimida ning rakenduse välimus võib vigane olla.",
"features": "%(brand)s kasutab mitmeid uusi brauseri-põhiseid tehnoloogiaid, mis ei ole veel sinu veebibrauseris toetatud.",
"summary": "%(brand)s ei toimi sinu brauseris",
"title": "Sellele brauserile puudub tugi"
},
"powered_by_matrix": "Põhineb Matrix'il",
"powered_by_matrix_with_logo": "Hajutatud ja krüpteeritud suhtlus- ning ühistöörakendus, mille aluseks on $matrixLogo",
"unknown_device": "Tundmatu seade",
"use_brand_on_mobile": "Kasuta rakendust %(brand)s nutiseadmes",
"web_default_device_name": "%(appName)s: %(browserName)s operatsioonisüsteemis %(osName)s",
"welcome_to_element": "Tere tulemast kasutama suhtlusrakendust Element"
}

View File

@ -1,27 +0,0 @@
{
"Dismiss": "Baztertu",
"Unknown device": "Gailu ezezaguna",
"Welcome to Element": "Ongi etorri Element mezularitzara",
"Sign In": "Hasi saioa",
"Create Account": "Sortu kontua",
"Explore rooms": "Arakatu gelak",
"Unexpected error preparing the app. See console for details.": "Ustekabeko errorea aplikazioa prestatzean. Ikusi xehetasunak kontsolan.",
"Invalid configuration: no default server specified.": "Konfigurazio baliogabea: Ez da lehenetsitako zerbitzaririk zehaztu.",
"The message from the parser is: %(message)s": "Prozesatzailearen mezua hau da: %(message)s",
"Invalid JSON": "JSON baliogabea",
"Go to your browser to complete Sign In": "Joan zure nabigatzailera izena ematen bukatzeko",
"Unable to load config file: please refresh the page to try again.": "Ezin izan da konfigurazio fitxategia kargatu: Saiatu orria birkargatzen.",
"Unsupported browser": "Onartu gabeko nabigatzailea",
"Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> for the best experience.": "Instalatu <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, edo <safariLink>Safari</safariLink> esperientzia hobe baterako.",
"You can continue using your current browser, but some or all features may not work and the look and feel of the application may be incorrect.": "Zure oraingo nabigatzailea erabiltzen jarraitu dezakezu, baina ezaugarri batzuk agian ez dute funtzionatuko eta itxura desegokia izan daiteke.",
"I understand the risks and wish to continue": "Arriskuak ulertzen ditut eta jarraitu nahi dut",
"Go to element.io": "Joan element.io gunera",
"Failed to start": "Huts egin du abiatzean",
"Your Element is misconfigured": "Zure Element ez dago ondo konfiguratuta",
"Your Element configuration contains invalid JSON. Please correct the problem and reload the page.": "Zure Element-en konfigurazioak JSON baliogabea dauka. Mesedez, konpondu arazoa eta birkargatu orria.",
"Download Completed": "Deskarga burututa",
"Open": "Ireki",
"Your browser can't run %(brand)s": "Zure nabigatzaileak ezin du %(brand)s exekutatu",
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "%(brand)s-(e)k zure oraingo nabigatzaile honek euskarririk ematen ez dien ezaugarri aurreratuak erabiltzen ditu.",
"Powered by Matrix": "Matrixekin egina"
}

View File

@ -1,31 +1,39 @@
{
"Unknown device": "دستگاه ناشناخته",
"Welcome to Element": "به Element خوش‌آمدید",
"Dismiss": "نادیده بگیر",
"Invalid JSON": "JSON اشتباه",
"Go to your browser to complete Sign In": "برای تکمیل ورود به مرورگر خود بروید",
"Sign In": "ورود",
"Create Account": "ایجاد حساب کاربری",
"Explore rooms": "جستجو در اتاق ها",
"Invalid configuration: no default server specified.": "پیکربندی نامعتبر: سرور پیشفرض مشخص نشده است.",
"Your Element is misconfigured": "Element شما پیکربندی نشده است",
"Your Element configuration contains invalid JSON. Please correct the problem and reload the page.": "پیکربندی المنت شما شامل JSON نا معتبر است. لطفا مشکل را اصلاح کنید و صفحه را بارگذاری مجدد کنید.",
"The message from the parser is: %(message)s": "پیام از طرف تجزیه کننده: %(message)s",
"Unable to load config file: please refresh the page to try again.": "قادر به بارگذاری فایل پیکربندی نیست: لطفا برای تلاش مجدد صفحه را تازه کنید.",
"Unexpected error preparing the app. See console for details.": "خطای غیر منتظره در آماده سازی برنامه. کنسول را برای جزئیات مشاهده کنید.",
"Download Completed": "بارگیری کامل شد",
"Open": "باز",
"Unsupported browser": "مرورگر پش‬تبانی نمی شود",
"Your browser can't run %(brand)s": "مرورگر شما نمی تواند %(brand)s را اجرا کند",
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "%(brand)s از ویژگی های پیشرفته مرورگر استفاده می کند که در مرورگر فعلی شما پشتیبانی نمی شوند.",
"Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> for the best experience.": "لطفا برای تجربه بهتر <chromeLink>کروم</chromeLink>، <firefoxLink>فایرفاکس</firefoxLink>، یا <safariLink>سافاری</safariLink> را نصب کنید.",
"You can continue using your current browser, but some or all features may not work and the look and feel of the application may be incorrect.": "شما می توانید با مرورگر فعلی خود ادامه دهید، اما ممکن است عملکرد تمامی یا برخی از قابلیت ها با اشکال روبرو شود و نمایش برنامه صحیح نباشد.",
"I understand the risks and wish to continue": "از خطرات این کار آگاهم و مایلم که ادامه بدهم",
"Go to element.io": "برو به element.io",
"Failed to start": "خطا در شروع",
"Powered by Matrix": "راه اندازی شده با استفاده از ماتریکس",
"Use %(brand)s on mobile": "از %(brand)s گوشی استفاده کنید",
"Decentralised, encrypted chat &amp; collaboration powered by $matrixLogo": "همکاری چت غیرمتمرکز و رمزگذاری شده &amp; توسعه یافته با استفاده از $matrixLogo",
"%(appName)s: %(browserName)s on %(osName)s": "%(appName)s: %(browserName)s: روی %(osName)s",
"%(brand)s Desktop: %(platformName)s": "%(brand)s دسکتاپ: %(platformName)s"
"action": {
"create_account": "ایجاد حساب کاربری",
"dismiss": "نادیده بگیر",
"explore_rooms": "جستجو در اتاق ها",
"open": "باز",
"sign_in": "ورود"
},
"auth": {
"sso_complete_in_browser_dialog_title": "برای تکمیل ورود به مرورگر خود بروید"
},
"desktop_default_device_name": "%(brand)s دسکتاپ: %(platformName)s",
"download_completed": "بارگیری کامل شد",
"error": {
"app_launch_unexpected_error": "خطای غیر منتظره در آماده سازی برنامه. کنسول را برای جزئیات مشاهده کنید.",
"cannot_load_config": "قادر به بارگذاری فایل پیکربندی نیست: لطفا برای تلاش مجدد صفحه را تازه کنید.",
"invalid_configuration_no_server": "پیکربندی نامعتبر: سرور پیشفرض مشخص نشده است.",
"invalid_json": "پیکربندی المنت شما شامل JSON نا معتبر است. لطفا مشکل را اصلاح کنید و صفحه را بارگذاری مجدد کنید.",
"invalid_json_detail": "پیام از طرف تجزیه کننده: %(message)s",
"invalid_json_generic": "JSON اشتباه",
"misconfigured": "Element شما پیکربندی نشده است"
},
"failed_to_start": "خطا در شروع",
"go_to_element_io": "برو به element.io",
"incompatible_browser": {
"browser_links": "لطفا برای تجربه بهتر <chromeLink>کروم</chromeLink>، <firefoxLink>فایرفاکس</firefoxLink>، یا <safariLink>سافاری</safariLink> را نصب کنید.",
"continue_warning": "از خطرات این کار آگاهم و مایلم که ادامه بدهم",
"feature_warning": "شما می توانید با مرورگر فعلی خود ادامه دهید، اما ممکن است عملکرد تمامی یا برخی از قابلیت ها با اشکال روبرو شود و نمایش برنامه صحیح نباشد.",
"features": "%(brand)s از ویژگی های پیشرفته مرورگر استفاده می کند که در مرورگر فعلی شما پشتیبانی نمی شوند.",
"summary": "مرورگر شما نمی تواند %(brand)s را اجرا کند",
"title": "مرورگر پش‬تبانی نمی شود"
},
"powered_by_matrix": "راه اندازی شده با استفاده از ماتریکس",
"powered_by_matrix_with_logo": "همکاری چت غیرمتمرکز و رمزگذاری شده &amp; توسعه یافته با استفاده از $matrixLogo",
"unknown_device": "دستگاه ناشناخته",
"use_brand_on_mobile": "از %(brand)s گوشی استفاده کنید",
"web_default_device_name": "%(appName)s: %(browserName)s: روی %(osName)s",
"welcome_to_element": "به Element خوش‌آمدید"
}

View File

@ -1,31 +1,39 @@
{
"Dismiss": "Hylkää",
"Unknown device": "Tuntematon laite",
"Welcome to Element": "Tervetuloa Element-sovellukseen",
"Sign In": "Kirjaudu",
"Create Account": "Luo tili",
"Explore rooms": "Selaa huoneita",
"Unexpected error preparing the app. See console for details.": "Odottamaton virhe sovellusta valmisteltaessa. Katso konsolista lisätietoja.",
"Invalid configuration: no default server specified.": "Virheellinen asetus: oletuspalvelinta ei ole määritetty.",
"The message from the parser is: %(message)s": "Viesti jäsentimeltä: %(message)s",
"Invalid JSON": "Virheellinen JSON",
"Unable to load config file: please refresh the page to try again.": "Asetustiedostoa ei voi ladata. Yritä uudelleen lataamalla sivu uudelleen.",
"Go to your browser to complete Sign In": "Tee kirjautuminen loppuun selaimessasi",
"Unsupported browser": "Selainta ei tueta",
"Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> for the best experience.": "Asenna <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink> tai <safariLink>Safari</safariLink>, jotta kaikki toimii parhaiten.",
"You can continue using your current browser, but some or all features may not work and the look and feel of the application may be incorrect.": "Voit käyttää edelleen nykyistä selaintasi, mutta jotkut tai kaikki ominaisuudet eivät ehkä toimi ja sovelluksen ulkoasu voi olla virheellinen.",
"I understand the risks and wish to continue": "Ymmärrän riskit ja haluan jatkaa",
"Failed to start": "Käynnistys ei onnistunut",
"Download Completed": "Lataus valmis",
"Open": "Avaa",
"Go to element.io": "Mene osoitteeseen riot.im",
"Your Element is misconfigured": "Elementisi asetukset ovat pielessä",
"Your Element configuration contains invalid JSON. Please correct the problem and reload the page.": "Element-asetuksesi sisältävät epäkelpoa JSONia. Korjaa ongelma ja lataa sivu uudelleen.",
"Powered by Matrix": "Moottorina Matrix",
"Your browser can't run %(brand)s": "%(brand)s ei toimi selaimessasi",
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "%(brand)s käyttää edistyneitä selaimen ominaisuuksia, joita nykyinen selaimesi ei tue.",
"Use %(brand)s on mobile": "Käytä %(brand)sia mobiilisti",
"Decentralised, encrypted chat &amp; collaboration powered by $matrixLogo": "Hajautettu, salattu keskustelu &amp; yhteistyö, taustavoimana $matrixLogo",
"%(appName)s: %(browserName)s on %(osName)s": "%(appName)s: %(browserName)s käyttöjärjestelmällä %(osName)s",
"%(brand)s Desktop: %(platformName)s": "%(brand)sin työpöytäversio: %(platformName)s"
"action": {
"create_account": "Luo tili",
"dismiss": "Hylkää",
"explore_rooms": "Selaa huoneita",
"open": "Avaa",
"sign_in": "Kirjaudu"
},
"auth": {
"sso_complete_in_browser_dialog_title": "Tee kirjautuminen loppuun selaimessasi"
},
"desktop_default_device_name": "%(brand)sin työpöytäversio: %(platformName)s",
"download_completed": "Lataus valmis",
"error": {
"app_launch_unexpected_error": "Odottamaton virhe sovellusta valmisteltaessa. Katso konsolista lisätietoja.",
"cannot_load_config": "Asetustiedostoa ei voi ladata. Yritä uudelleen lataamalla sivu uudelleen.",
"invalid_configuration_no_server": "Virheellinen asetus: oletuspalvelinta ei ole määritetty.",
"invalid_json": "Element-asetuksesi sisältävät epäkelpoa JSONia. Korjaa ongelma ja lataa sivu uudelleen.",
"invalid_json_detail": "Viesti jäsentimeltä: %(message)s",
"invalid_json_generic": "Virheellinen JSON",
"misconfigured": "Elementisi asetukset ovat pielessä"
},
"failed_to_start": "Käynnistys ei onnistunut",
"go_to_element_io": "Mene osoitteeseen riot.im",
"incompatible_browser": {
"browser_links": "Asenna <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink> tai <safariLink>Safari</safariLink>, jotta kaikki toimii parhaiten.",
"continue_warning": "Ymmärrän riskit ja haluan jatkaa",
"feature_warning": "Voit käyttää edelleen nykyistä selaintasi, mutta jotkut tai kaikki ominaisuudet eivät ehkä toimi ja sovelluksen ulkoasu voi olla virheellinen.",
"features": "%(brand)s käyttää edistyneitä selaimen ominaisuuksia, joita nykyinen selaimesi ei tue.",
"summary": "%(brand)s ei toimi selaimessasi",
"title": "Selainta ei tueta"
},
"powered_by_matrix": "Moottorina Matrix",
"powered_by_matrix_with_logo": "Hajautettu, salattu keskustelu &amp; yhteistyö, taustavoimana $matrixLogo",
"unknown_device": "Tuntematon laite",
"use_brand_on_mobile": "Käytä %(brand)sia mobiilisti",
"web_default_device_name": "%(appName)s: %(browserName)s käyttöjärjestelmällä %(osName)s",
"welcome_to_element": "Tervetuloa Element-sovellukseen"
}

View File

@ -1,32 +1,40 @@
{
"Dismiss": "Ignorer",
"Unknown device": "Appareil inconnu",
"Welcome to Element": "Bienvenue sur Element",
"Sign In": "Se connecter",
"Create Account": "Créer un compte",
"Explore rooms": "Parcourir les salons",
"Unexpected error preparing the app. See console for details.": "Une erreur inattendue est survenue pendant la préparation de lapplication. Consultez la console pour avoir des détails.",
"Invalid configuration: no default server specified.": "Configuration invalide : aucun serveur par défaut indiqué.",
"The message from the parser is: %(message)s": "Le message de lanalyseur est : %(message)s",
"Invalid JSON": "JSON non valide",
"Go to your browser to complete Sign In": "Utilisez votre navigateur pour terminer la connexion",
"Unable to load config file: please refresh the page to try again.": "Impossible de charger le fichier de configuration : rechargez la page pour réessayer.",
"Unsupported browser": "Navigateur non pris en charge",
"Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> for the best experience.": "Veuillez installer <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink> ou <safariLink>Safari</safariLink> pour une expérience optimale.",
"You can continue using your current browser, but some or all features may not work and the look and feel of the application may be incorrect.": "Vous pouvez continuer à utiliser votre navigateur actuel, mais vous risquez de trouver que certaines fonctionnalités et/ou lapparence de lapplication sont incorrectes.",
"I understand the risks and wish to continue": "Je comprends les risques et souhaite continuer",
"Go to element.io": "Aller vers element.io",
"Failed to start": "Échec au démarrage",
"Download Completed": "Téléchargement terminé",
"Open": "Ouvrir",
"Your Element is misconfigured": "Votre Element est mal configuré",
"Your Element configuration contains invalid JSON. Please correct the problem and reload the page.": "La configuration de votre Element contient du JSON invalide. Veuillez corriger le problème et recharger la page.",
"Your browser can't run %(brand)s": "Votre navigateur ne peut pas exécuter %(brand)s",
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "%(brand)s nécessite des fonctionnalités avancées que votre navigateur actuel ne prend pas en charge.",
"Powered by Matrix": "Propulsé par Matrix",
"Use %(brand)s on mobile": "Utiliser %(brand)s sur téléphone",
"Decentralised, encrypted chat &amp; collaboration powered by $matrixLogo": "Messagerie décentralisée, chiffrée &amp; une collaboration alimentée par $matrixLogo",
"%(appName)s: %(browserName)s on %(osName)s": "%(appName)s : %(browserName)s pour %(osName)s",
"%(brand)s Desktop: %(platformName)s": "%(brand)s pour bureau : %(platformName)s",
"Invalid configuration: a default_hs_url can't be specified along with default_server_name or default_server_config": "Configuration invalide : default_hs_url ne peut pas être défini en même temps que default_server_name ou default_server_config"
"action": {
"create_account": "Créer un compte",
"dismiss": "Ignorer",
"explore_rooms": "Parcourir les salons",
"open": "Ouvrir",
"sign_in": "Se connecter"
},
"auth": {
"sso_complete_in_browser_dialog_title": "Utilisez votre navigateur pour terminer la connexion"
},
"desktop_default_device_name": "%(brand)s pour bureau : %(platformName)s",
"download_completed": "Téléchargement terminé",
"error": {
"app_launch_unexpected_error": "Une erreur inattendue est survenue pendant la préparation de lapplication. Consultez la console pour avoir des détails.",
"cannot_load_config": "Impossible de charger le fichier de configuration : rechargez la page pour réessayer.",
"invalid_configuration_mixed_server": "Configuration invalide : default_hs_url ne peut pas être défini en même temps que default_server_name ou default_server_config",
"invalid_configuration_no_server": "Configuration invalide : aucun serveur par défaut indiqué.",
"invalid_json": "La configuration de votre Element contient du JSON invalide. Veuillez corriger le problème et recharger la page.",
"invalid_json_detail": "Le message de lanalyseur est : %(message)s",
"invalid_json_generic": "JSON non valide",
"misconfigured": "Votre Element est mal configuré"
},
"failed_to_start": "Échec au démarrage",
"go_to_element_io": "Aller vers element.io",
"incompatible_browser": {
"browser_links": "Veuillez installer <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink> ou <safariLink>Safari</safariLink> pour une expérience optimale.",
"continue_warning": "Je comprends les risques et souhaite continuer",
"feature_warning": "Vous pouvez continuer à utiliser votre navigateur actuel, mais vous risquez de trouver que certaines fonctionnalités et/ou lapparence de lapplication sont incorrectes.",
"features": "%(brand)s nécessite des fonctionnalités avancées que votre navigateur actuel ne prend pas en charge.",
"summary": "Votre navigateur ne peut pas exécuter %(brand)s",
"title": "Navigateur non pris en charge"
},
"powered_by_matrix": "Propulsé par Matrix",
"powered_by_matrix_with_logo": "Messagerie décentralisée, chiffrée &amp; une collaboration alimentée par $matrixLogo",
"unknown_device": "Appareil inconnu",
"use_brand_on_mobile": "Utiliser %(brand)s sur téléphone",
"web_default_device_name": "%(appName)s : %(browserName)s pour %(osName)s",
"welcome_to_element": "Bienvenue sur Element"
}

View File

@ -1,28 +0,0 @@
{
"Sign In": "Oanmelde",
"Failed to start": "Opstarten mislearre",
"You can continue using your current browser, but some or all features may not work and the look and feel of the application may be incorrect.": "Jo kinne fierder gean mei jo eigen browser, mar guon funksjes kinne net wurkje en uterlik kin de applikaasje der ôfwikend útsjen.",
"Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> for the best experience.": "Graach <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, of<safariLink>Safari</safariLink> ynstallearje foar de beste ûnderfining.",
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "%(brand)s brûkt avansearre browserfunksjes dyt net stipe wurde troch de browser dyt jo no brûke.",
"Powered by Matrix": "Mooglik makke troch Matrix",
"Unexpected error preparing the app. See console for details.": "Unferwachte flater by it klearmeitsjen fan de applikaasje. Sjoch yn de console foar details.",
"The message from the parser is: %(message)s": "It berjocht fan de ferwurker is: %(message)s",
"Your Element configuration contains invalid JSON. Please correct the problem and reload the page.": "Jo Element-konfiguraasje hat ûnjildige JSON. Nei dat jo dit oplost ha, kin dizze side ferfarske wurde.",
"Use %(brand)s on mobile": "Brûk %(brand)s op mobyl",
"Go to your browser to complete Sign In": "Gean nei jo browser om it ynskriuwen te foltôgjen",
"Download Completed": "Download foltôge",
"Unable to load config file: please refresh the page to try again.": "Kin konfiguraasjebestân net lade: ferfarskje de side en probearje it nochris.",
"Dismiss": "Slute",
"Explore rooms": "Keamers ûntdekke",
"Create Account": "Registrearje",
"Welcome to Element": "Wolkom by Element",
"I understand the risks and wish to continue": "Ik begryp de risiko's en wol graach fierder gean",
"Go to element.io": "Gean nei element.io",
"Your browser can't run %(brand)s": "Jo browser kin %(brand)s net útfiere",
"Unsupported browser": "Net stipe browser",
"Unknown device": "Unbekend apparaat",
"Open": "Iepenje",
"Invalid JSON": "Unjildige JSON",
"Your Element is misconfigured": "Jo Element is net goed konfigurearre",
"Invalid configuration: no default server specified.": "Unjildiche konfiguraasje: gjin standertserver selektearre."
}

View File

@ -1,28 +0,0 @@
{
"Unknown device": "Gléas nár aithníodh",
"Dismiss": "Cuir uait",
"Welcome to Element": "Fáilte romhat chuig Element",
"Sign In": "Sínigh Isteach",
"Create Account": "Déan cuntas a chruthú",
"Explore rooms": "Breathnaigh thart ar na seomraí",
"Your browser can't run %(brand)s": "Níl do bhrabhsálaí comhoiriúnach do %(brand)s",
"Go to your browser to complete Sign In": "Oscail do bhrabhsálaí agus críochnaigh an clárú",
"Your Element configuration contains invalid JSON. Please correct the problem and reload the page.": "Coinníonn do chumraíocht JSON neamhbhailí. Ceartaigh an fadhb agus athlódáil an leathanach le do thoil.",
"Your Element is misconfigured": "Níl do fheidhmchlár Element cumraithe i gceart",
"Failed to start": "Theip chun tosú",
"I understand the risks and wish to continue": "Tuigim na rioscaí agus ba mhaith liom lean ar aghaidh",
"You can continue using your current browser, but some or all features may not work and the look and feel of the application may be incorrect.": "An féidir leat úsáid do bhrabhsálaí reatha, ach nár oibrí roinnt nó gach gné agus nár thaispeántar an feidhmchlár i gceart.",
"Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> for the best experience.": "Suiteáil <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> chun an taithí is fearr a fháil.",
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "Úsáideann %(brand)s gnéithe ardforbartha nach bhfuil ar fáil faoi do bhrabhsálaí reatha.",
"Unsupported browser": "Brabhsálaí gan tacaíocht",
"Unexpected error preparing the app. See console for details.": "Earráid nuair an feidhmchlár a hullmhú. Feic sa consól le haghaidh eolas.",
"Unable to load config file: please refresh the page to try again.": "Ní féidir an comhad cumraíochta a lódáil. Athnuaigh an leathanach chun déanamh iarracht arís le do thoil.",
"Download Completed": "Íoslódáil críochnaithe",
"Invalid JSON": "JSON neamhbhailí",
"The message from the parser is: %(message)s": "Is í an teachtaireacht as an parsálaí: %(message)s",
"Invalid configuration: no default server specified.": "Cumraíocht neamhbhailí: Níl aon freastalaí réamhshocraithe a sonrú.",
"Powered by Matrix": "Cumhachtaithe ag Matrix",
"Go to element.io": "Téigh go element.io",
"Open": "Oscail",
"Use %(brand)s on mobile": "Úsáid %(brand)s ar guthán póca"
}

View File

@ -1,31 +1,39 @@
{
"Dismiss": "Rexeitar",
"Unknown device": "Dispositivo descoñecido",
"Welcome to Element": "Benvida/o a Element",
"Sign In": "Acceder",
"Create Account": "Crear conta",
"Explore rooms": "Explorar salas",
"The message from the parser is: %(message)s": "A mensaxe desde o intérprete é: %(message)s",
"Invalid JSON": "JSON non válido",
"Unexpected error preparing the app. See console for details.": "Fallo non agardado ao preparar a app. Detalles na consola.",
"Invalid configuration: no default server specified.": "Configuración non válida: non se indicou servidor por defecto.",
"Unable to load config file: please refresh the page to try again.": "Non se cargou o ficheiro de configuración: actualiza a páxina para reintentalo.",
"Go to your browser to complete Sign In": "Abre o navegador para realizar a Conexión",
"Unsupported browser": "Navegador non soportado",
"Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> for the best experience.": "Instala <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, ou <safariLink>Safari</safariLink> para ter unha mellor experiencia.",
"You can continue using your current browser, but some or all features may not work and the look and feel of the application may be incorrect.": "Podes continuar co teu navegador, pero algunhas características poderían non funcionar e o aspecto da aplicación podería non ser o correcto.",
"I understand the risks and wish to continue": "Entendo os riscos e desexo continuar",
"Go to element.io": "Ir a element.io",
"Failed to start": "Fallou o inicio",
"Download Completed": "Descarga realizada",
"Open": "Abrir",
"Your Element is misconfigured": "Element non está ben configurado",
"Your Element configuration contains invalid JSON. Please correct the problem and reload the page.": "A configuración de Element contén JSON non válido. Corrixe o problema e recarga a páxina.",
"Your browser can't run %(brand)s": "O teu navegador non pode executar %(brand)s",
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "%(brand)s utiliza características avanzadas do navegador que non están dispoñibles no teu navegador.",
"Powered by Matrix": "Funciona grazas a Matrix",
"Use %(brand)s on mobile": "Utiliza %(brand)s no móbil",
"Decentralised, encrypted chat &amp; collaboration powered by $matrixLogo": "Conversas &amp; colaboración descentralizadas e cifradas grazas a $matrixLogo",
"%(appName)s: %(browserName)s on %(osName)s": "%(appName)s: %(browserName)s en %(osName)s",
"%(brand)s Desktop: %(platformName)s": "%(brand)s para Escritorio: %(platformName)s"
"action": {
"create_account": "Crear conta",
"dismiss": "Rexeitar",
"explore_rooms": "Explorar salas",
"open": "Abrir",
"sign_in": "Acceder"
},
"auth": {
"sso_complete_in_browser_dialog_title": "Abre o navegador para realizar a Conexión"
},
"desktop_default_device_name": "%(brand)s para Escritorio: %(platformName)s",
"download_completed": "Descarga realizada",
"error": {
"app_launch_unexpected_error": "Fallo non agardado ao preparar a app. Detalles na consola.",
"cannot_load_config": "Non se cargou o ficheiro de configuración: actualiza a páxina para reintentalo.",
"invalid_configuration_no_server": "Configuración non válida: non se indicou servidor por defecto.",
"invalid_json": "A configuración de Element contén JSON non válido. Corrixe o problema e recarga a páxina.",
"invalid_json_detail": "A mensaxe desde o intérprete é: %(message)s",
"invalid_json_generic": "JSON non válido",
"misconfigured": "Element non está ben configurado"
},
"failed_to_start": "Fallou o inicio",
"go_to_element_io": "Ir a element.io",
"incompatible_browser": {
"browser_links": "Instala <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, ou <safariLink>Safari</safariLink> para ter unha mellor experiencia.",
"continue_warning": "Entendo os riscos e desexo continuar",
"feature_warning": "Podes continuar co teu navegador, pero algunhas características poderían non funcionar e o aspecto da aplicación podería non ser o correcto.",
"features": "%(brand)s utiliza características avanzadas do navegador que non están dispoñibles no teu navegador.",
"summary": "O teu navegador non pode executar %(brand)s",
"title": "Navegador non soportado"
},
"powered_by_matrix": "Funciona grazas a Matrix",
"powered_by_matrix_with_logo": "Conversas &amp; colaboración descentralizadas e cifradas grazas a $matrixLogo",
"unknown_device": "Dispositivo descoñecido",
"use_brand_on_mobile": "Utiliza %(brand)s no móbil",
"web_default_device_name": "%(appName)s: %(browserName)s en %(osName)s",
"welcome_to_element": "Benvida/o a Element"
}

View File

@ -1,31 +1,39 @@
{
"Dismiss": "התעלם",
"Unknown device": "מכשיר לא ידוע",
"Welcome to Element": "ברוכים הבאים ל Element",
"Invalid JSON": "JSON לא חוקי",
"Invalid configuration: no default server specified.": "תצורה שגויה: לא צוין שרת ברירת מחדל.",
"Go to your browser to complete Sign In": "עבור לדפדפן להמשך ההתחברות",
"Explore rooms": "גלה חדרים",
"Create Account": "משתמש חדש",
"Sign In": "התחברות",
"Open": "פתח",
"Download Completed": "ההורדה הושלמה",
"Unexpected error preparing the app. See console for details.": "שגיאה לא צפויה במהלך טעינת האפליקציה. ראו קונסול לפרטים נוספים.",
"Unable to load config file: please refresh the page to try again.": "לא ניתן לטעון את קובץ ההגדרות: יש לרענן את הדף כדי לנסות שנית.",
"Your Element configuration contains invalid JSON. Please correct the problem and reload the page.": "האלמנט מכיל הגדרת JSON שגויה, אנא תקנו את הבעיה ואתחלו את הדף.",
"Your Element is misconfigured": "Element אינו מוגדר תקין",
"Go to element.io": "חזור לאתר הראשי: element.io",
"I understand the risks and wish to continue": "הסיכונים מובנים לי ואני מעוניינ/ת להמשיך",
"You can continue using your current browser, but some or all features may not work and the look and feel of the application may be incorrect.": "ניתן להמשיך ולהשתמש בדפדפן זה, אך ייתכן שחלק מן התכונות והמאפיינים לא יעבדו כשורה או ייראו כשגויים.",
"Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> for the best experience.": "נא התקן את דפדפן <chromeLink>כרום</chromeLink>, <firefoxLink>פיירפוקס</firefoxLink> או <safariLink>סאפרי</safariLink> בשביל החוויה הטובה ביותר.",
"Failed to start": "כשל בהעלאת התוכנה",
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "%(brand)s משתמש בתכונות דפדפן מתקדמות שאינן נתמכות בדפדפן הנוכחי שלך.",
"Your browser can't run %(brand)s": "הדפדפן שלך לא יכול להריץ %(brand)s",
"Unsupported browser": "דפדפן לא נתמך",
"Powered by Matrix": "מופעל על ידי מטריקס",
"The message from the parser is: %(message)s": "ההודעה מהמנתח היא: %(message)s",
"Use %(brand)s on mobile": "השתמש ב-%(brand)s במכשיר הנייד",
"Decentralised, encrypted chat &amp; collaboration powered by $matrixLogo": "צ'אט מבוזר ומוצפן &amp; מופעל בשיתוף פעולה ע\"י $matrixLogo",
"%(appName)s: %(browserName)s on %(osName)s": "%(appName)s: %(browserName)s עַל %(osName)s",
"%(brand)s Desktop: %(platformName)s": "%(brand)s שולחן עבודה: %(platformName)s"
"action": {
"create_account": "משתמש חדש",
"dismiss": "התעלם",
"explore_rooms": "גלה חדרים",
"open": "פתח",
"sign_in": "התחברות"
},
"auth": {
"sso_complete_in_browser_dialog_title": "עבור לדפדפן להמשך ההתחברות"
},
"desktop_default_device_name": "%(brand)s שולחן עבודה: %(platformName)s",
"download_completed": "ההורדה הושלמה",
"error": {
"app_launch_unexpected_error": "שגיאה לא צפויה במהלך טעינת האפליקציה. ראו קונסול לפרטים נוספים.",
"cannot_load_config": "לא ניתן לטעון את קובץ ההגדרות: יש לרענן את הדף כדי לנסות שנית.",
"invalid_configuration_no_server": "תצורה שגויה: לא צוין שרת ברירת מחדל.",
"invalid_json": "האלמנט מכיל הגדרת JSON שגויה, אנא תקנו את הבעיה ואתחלו את הדף.",
"invalid_json_detail": "ההודעה מהמנתח היא: %(message)s",
"invalid_json_generic": "JSON לא חוקי",
"misconfigured": "Element אינו מוגדר תקין"
},
"failed_to_start": "כשל בהעלאת התוכנה",
"go_to_element_io": "חזור לאתר הראשי: element.io",
"incompatible_browser": {
"browser_links": "נא התקן את דפדפן <chromeLink>כרום</chromeLink>, <firefoxLink>פיירפוקס</firefoxLink> או <safariLink>סאפרי</safariLink> בשביל החוויה הטובה ביותר.",
"continue_warning": "הסיכונים מובנים לי ואני מעוניינ/ת להמשיך",
"feature_warning": "ניתן להמשיך ולהשתמש בדפדפן זה, אך ייתכן שחלק מן התכונות והמאפיינים לא יעבדו כשורה או ייראו כשגויים.",
"features": "%(brand)s משתמש בתכונות דפדפן מתקדמות שאינן נתמכות בדפדפן הנוכחי שלך.",
"summary": "הדפדפן שלך לא יכול להריץ %(brand)s",
"title": "דפדפן לא נתמך"
},
"powered_by_matrix": "מופעל על ידי מטריקס",
"powered_by_matrix_with_logo": "צ'אט מבוזר ומוצפן &amp; מופעל בשיתוף פעולה ע\"י $matrixLogo",
"unknown_device": "מכשיר לא ידוע",
"use_brand_on_mobile": "השתמש ב-%(brand)s במכשיר הנייד",
"web_default_device_name": "%(appName)s: %(browserName)s עַל %(osName)s",
"welcome_to_element": "ברוכים הבאים ל Element"
}

View File

@ -1,28 +0,0 @@
{
"Unknown device": "अज्ञात यन्त्र",
"Dismiss": "खारिज",
"Welcome to Element": "Element में आपका स्वागत है",
"Sign In": "साइन करना",
"Create Account": "खाता बनाएं",
"Explore rooms": "रूम का अन्वेषण करें",
"Failed to start": "प्रारंभ करने में विफल",
"Go to element.io": "element.io पर जाएं",
"I understand the risks and wish to continue": "मैं जोखिमों को समझता हूं और जारी रखना चाहता हूं",
"You can continue using your current browser, but some or all features may not work and the look and feel of the application may be incorrect.": "आप अपने वर्तमान ब्राउज़र का उपयोग जारी रख सकते हैं, लेकिन हो सकता है कि कुछ या सभी सुविधाएं काम न करें और एप्लिकेशन का रंगरूप गलत हो सकता है।",
"Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> for the best experience.": "सर्वोत्तम अनुभव के लिए कृपया <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, या <safariLink>Safari</safariLink> इंस्टॉल करें।",
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "%(brand)s उन्नत ब्राउज़र सुविधाओं का उपयोग करते हैं जो आपके वर्तमान ब्राउज़र द्वारा समर्थित नहीं हैं।",
"Your browser can't run %(brand)s": "आपका ब्राउज़र %(brand)s को नहीं चला सकता",
"Use %(brand)s on mobile": "मोबाइल पर %(brand)s का प्रयोग करें",
"Unsupported browser": "असमर्थित ब्राउज़र",
"Powered by Matrix": "मैट्रिक्स द्वारा संचालित",
"Go to your browser to complete Sign In": "साइन इन पूरा करने के लिए अपने ब्राउज़र पर जाएं",
"Open": "खुला",
"Download Completed": "डाउनलोड सम्पन्न हुआ",
"Unexpected error preparing the app. See console for details.": "ऐप्लिकेशन तैयार करने में अनपेक्षित गड़बड़ी हुई. विवरण के लिए कंसोल देखें।",
"Unable to load config file: please refresh the page to try again.": "कॉन्फ़िग फ़ाइल लोड करने में असमर्थ: कृपया पुन: प्रयास करने के लिए पृष्ठ को रीफ़्रेश करें।",
"Invalid JSON": "अमान्य JSON",
"The message from the parser is: %(message)s": "पार्सर का संदेश है: %(message)s",
"Your Element configuration contains invalid JSON. Please correct the problem and reload the page.": "आपके एलीमेंट कॉन्फ़िगरेशन में अमान्य JSON है. कृपया समस्या को ठीक करें और पृष्ठ को पुनः लोड करें।",
"Your Element is misconfigured": "आपका तत्व गलत कॉन्फ़िगर किया गया है",
"Invalid configuration: no default server specified.": "अमान्य कॉन्फ़िगरेशन: कोई डिफ़ॉल्ट सर्वर निर्दिष्ट नहीं है।"
}

View File

@ -1,5 +0,0 @@
{
"Unknown device": "Nepoznati uređaj",
"Dismiss": "Odbaci",
"Welcome to Element": "Dobrodošli u Element"
}

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