diff --git a/.github/actions/build-frontend/action.yml b/.github/actions/build-frontend/action.yml index dd8a30703..3118e4f22 100644 --- a/.github/actions/build-frontend/action.yml +++ b/.github/actions/build-frontend/action.yml @@ -1,26 +1,20 @@ +# Copyright 2025, 2026 Element Creations Ltd. # Copyright 2025 New Vector Ltd. # # SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial # Please see LICENSE files in the repository root for full details. name: Build the frontend assets -description: Installs Node.js and builds the frontend assets from the frontend directory +description: Installs pnpm, Node.js and the dependencies, then builds the frontend assets runs: using: composite steps: - - name: Install pnpm - uses: pnpm/action-setup@v6.0.5 - - - name: Install Node - uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0 + - name: Install pnpm, Node.js and the dependencies + uses: pnpm/setup@703c52620218391530e48b9e8870d5c0082e1b9b # v2.1.0 with: - node-version-file: .node-version - cache: "pnpm" - - - name: Install dependencies - run: pnpm install --frozen-lockfile - shell: sh + cache: true + require-lockfile: true - name: Build the frontend assets run: pnpm --filter mas-frontend run build diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 1735e4cb9..cfc0fe5aa 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,3 +1,4 @@ +# Copyright 2025, 2026 Element Creations Ltd. # Copyright 2025 New Vector Ltd. # # SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial @@ -43,6 +44,17 @@ updates: cooldown: default-days: 14 + # Rust releases every six weeks; the cooldown lets the .1 point release land first. + - package-ecosystem: "rust-toolchain" + directory: "/" + labels: + - "A-Dependencies" + - "Z-Deps-Backend" + schedule: + interval: "weekly" + cooldown: + default-days: 14 + - package-ecosystem: "github-actions" directory: "/" labels: diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index d2e412bc7..4a5bbbb13 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -1,3 +1,4 @@ +# Copyright 2025, 2026 Element Creations Ltd. # Copyright 2025 New Vector Ltd. # # SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial @@ -123,9 +124,8 @@ jobs: persist-credentials: false - name: Install Rust toolchain - # Install the minimal toolchain, which includes rustc, rustdoc, and cargo. run: | - rustup toolchain install stable --profile minimal + rustup toolchain install rustup target add ${{ matrix.target }} - name: Setup sccache @@ -137,7 +137,7 @@ jobs: version: 0.13.0 - name: Install cargo-zigbuild - uses: taiki-e/install-action@b8cecb83565409bcc297b2df6e77f030b2a468d5 # v2 + uses: taiki-e/install-action@b6ff580856c41316412a0b9b60540fbc6f8c82cc # v2 with: tool: cargo-zigbuild diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 0640b7fb9..4b6eff083 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -68,17 +68,11 @@ jobs: with: persist-credentials: false - - name: Install pnpm - uses: pnpm/action-setup@v6.0.9 - - - name: Install Node - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + - name: Install pnpm, Node.js and the dependencies + uses: pnpm/setup@703c52620218391530e48b9e8870d5c0082e1b9b # v2.1.0 with: - node-version-file: .node-version - cache: "pnpm" - - - name: Install dependencies - run: pnpm install --frozen-lockfile + cache: true + require-lockfile: true - name: Lint run: pnpm --filter mas-frontend run lint @@ -96,17 +90,11 @@ jobs: with: persist-credentials: false - - name: Install pnpm - uses: pnpm/action-setup@v6.0.9 - - - name: Install Node - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + - name: Install pnpm, Node.js and the dependencies + uses: pnpm/setup@703c52620218391530e48b9e8870d5c0082e1b9b # v2.1.0 with: - node-version-file: .node-version - cache: "pnpm" - - - name: Install dependencies - run: pnpm install --frozen-lockfile + cache: true + require-lockfile: true - name: Test run: pnpm --filter mas-frontend test @@ -124,17 +112,11 @@ jobs: with: persist-credentials: false - - name: Install pnpm - uses: pnpm/action-setup@v6.0.9 - - - name: Install Node - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + - name: Install pnpm, Node.js and the dependencies + uses: pnpm/setup@703c52620218391530e48b9e8870d5c0082e1b9b # v2.1.0 with: - node-version-file: .node-version - cache: "pnpm" - - - name: Install dependencies - run: pnpm install --frozen-lockfile + cache: true + require-lockfile: true - name: Check for unused dependencies run: pnpm --filter mas-frontend run knip @@ -153,15 +135,12 @@ jobs: persist-credentials: false - name: Install Rust toolchain - # Install the minimal toolchain, which includes rustc, rustdoc, and cargo. - # Then install rustfmt for `cargo fmt`. - # - # --override sets this as the default rust toolchain version in this directory. + # rustfmt runs on nightly because .rustfmt.toml uses nightly-only options. run: | - rustup toolchain install nightly --profile minimal --component rustfmt --override + rustup toolchain install nightly --profile minimal --component rustfmt - name: Check style - run: cargo fmt --all -- --check + run: cargo +nightly fmt --all -- --check cargo-deny: name: Run `cargo deny` checks @@ -182,8 +161,6 @@ jobs: - name: Run `cargo-deny` uses: EmbarkStudios/cargo-deny-action@3c6349835b2b7b196a839186cb8b78e02f7b5f25 # v2.1.1 - with: - rust-version: stable check-schema: name: Check schema @@ -200,7 +177,7 @@ jobs: - name: Install Rust toolchain run: | - rustup toolchain install stable + rustup toolchain install - name: Setup sccache uses: mozilla-actions/sccache-action@9e7fa8a12102821edf02ca5dbea1acd0f89a2696 # v0.0.10 @@ -241,11 +218,7 @@ jobs: persist-credentials: false - name: Install Rust toolchain - # Install the minimal toolchain with clippy, pinned to a version kept in - # sync with Dockerfile. - # - # --override sets this as the default rust toolchain version in this directory. - run: rustup toolchain install 1.96.0 --profile minimal --component clippy --override + run: rustup toolchain install - uses: ./.github/actions/build-policies @@ -270,11 +243,10 @@ jobs: persist-credentials: false - name: Install Rust toolchain - # Install the minimal toolchain, which includes rustc, rustdoc, and cargo. - run: rustup toolchain install stable --profile minimal + run: rustup toolchain install - name: Install nextest - uses: taiki-e/install-action@b8cecb83565409bcc297b2df6e77f030b2a468d5 # v2 + uses: taiki-e/install-action@b6ff580856c41316412a0b9b60540fbc6f8c82cc # v2 with: tool: cargo-nextest @@ -293,7 +265,7 @@ jobs: path: nextest-archive.tar.zst test: - name: Run test suite with Rust stable + name: Run test suite needs: [rustfmt, opa-lint, compile-test-artifacts] runs-on: ubuntu-24.04 @@ -326,11 +298,10 @@ jobs: persist-credentials: false - name: Install Rust toolchain - # Install the minimal toolchain, which includes rustc, rustdoc, and cargo. - run: rustup toolchain install stable --profile minimal + run: rustup toolchain install - name: Install nextest - uses: taiki-e/install-action@b8cecb83565409bcc297b2df6e77f030b2a468d5 # v2 + uses: taiki-e/install-action@b6ff580856c41316412a0b9b60540fbc6f8c82cc # v2 with: tool: cargo-nextest @@ -410,7 +381,7 @@ jobs: - name: Upload logs if: ${{ failure() }} - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: ess-helm-logs path: ess-helm-logs diff --git a/.github/workflows/coverage.yaml b/.github/workflows/coverage.yaml index 8393d8e92..aab2d235e 100644 --- a/.github/workflows/coverage.yaml +++ b/.github/workflows/coverage.yaml @@ -1,3 +1,4 @@ +# Copyright 2025, 2026 Element Creations Ltd. # Copyright 2025 New Vector Ltd. # # SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial @@ -107,17 +108,17 @@ jobs: persist-credentials: false - name: Install Rust toolchain - # Install the minimal toolchain, which includes rustc, rustdoc, and cargo. - # # llvm-tools-preview installs some llvm tools, which are needed for `grcov` below. # See https://github.com/rust-lang/rust/issues/85658 for stability tracking issue. - run: rustup toolchain install stable --profile minimal --component llvm-tools-preview + run: | + rustup toolchain install + rustup component add llvm-tools-preview - name: Setup sccache uses: mozilla-actions/sccache-action@9e7fa8a12102821edf02ca5dbea1acd0f89a2696 # v0.0.10 - name: Install grcov - uses: taiki-e/install-action@b8cecb83565409bcc297b2df6e77f030b2a468d5 # v2 + uses: taiki-e/install-action@b6ff580856c41316412a0b9b60540fbc6f8c82cc # v2 with: tool: grcov diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index e2020ef33..19e3d7d67 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -1,3 +1,4 @@ +# Copyright 2025, 2026 Element Creations Ltd. # Copyright 2025 New Vector Ltd. # # SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial @@ -30,25 +31,21 @@ jobs: persist-credentials: false - name: Install Rust toolchain - # Install the minimal toolchain, which includes rustc, rustdoc, and cargo - run: rustup toolchain install stable --profile minimal + run: rustup toolchain install - name: Setup sccache uses: mozilla-actions/sccache-action@9e7fa8a12102821edf02ca5dbea1acd0f89a2696 # v0.0.10 - name: Install mdbook - uses: taiki-e/install-action@b8cecb83565409bcc297b2df6e77f030b2a468d5 # v2 + uses: taiki-e/install-action@b6ff580856c41316412a0b9b60540fbc6f8c82cc # v2 with: tool: mdbook - - name: Install pnpm - uses: pnpm/action-setup@v6.0.9 - - - name: Install Node - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + - name: Install pnpm, Node.js and the dependencies + uses: pnpm/setup@703c52620218391530e48b9e8870d5c0082e1b9b # v2.1.0 with: - node-version-file: .node-version - cache: "pnpm" + cache: true + require-lockfile: true - name: Build the documentation run: sh misc/build-docs.sh diff --git a/.github/workflows/release-branch.yaml b/.github/workflows/release-branch.yaml index 68788d1bb..108617952 100644 --- a/.github/workflows/release-branch.yaml +++ b/.github/workflows/release-branch.yaml @@ -1,3 +1,4 @@ +# Copyright 2025, 2026 Element Creations Ltd. # Copyright 2025 New Vector Ltd. # # SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial @@ -39,20 +40,13 @@ jobs: persist-credentials: false - name: Install Rust toolchain - # Only the minimal profile is required here to run `cargo metadata` - run: rustup toolchain install stable --profile minimal + run: rustup toolchain install - - name: Install pnpm - uses: pnpm/action-setup@v6.0.9 - - - name: Install Node - uses: actions/setup-node@v7.0.0 + - name: Install pnpm, Node.js and the dependencies + uses: pnpm/setup@703c52620218391530e48b9e8870d5c0082e1b9b # v2.1.0 with: - node-version-file: .node-version - cache: "pnpm" - - - name: Install dependencies - run: pnpm install --frozen-lockfile + cache: true + require-lockfile: true - name: Compute the new minor RC id: next @@ -80,17 +74,11 @@ jobs: with: persist-credentials: false - - name: Install pnpm - uses: pnpm/action-setup@v6.0.9 - - - name: Install Node - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + - name: Install pnpm, Node.js and the dependencies + uses: pnpm/setup@703c52620218391530e48b9e8870d5c0082e1b9b # v2.1.0 with: - node-version-file: .node-version - cache: "pnpm" - - - name: Install dependencies - run: pnpm install --frozen-lockfile + cache: true + require-lockfile: true - name: Create a new branch in Localazy run: pnpm exec localazy branch -w "$LOCALAZY_WRITE_KEY" create main "$BRANCH" diff --git a/.github/workflows/release-bump.yaml b/.github/workflows/release-bump.yaml index d5467d5d8..b1b2c4d3e 100644 --- a/.github/workflows/release-bump.yaml +++ b/.github/workflows/release-bump.yaml @@ -1,3 +1,4 @@ +# Copyright 2025, 2026 Element Creations Ltd. # Copyright 2025 New Vector Ltd. # # SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial @@ -38,20 +39,13 @@ jobs: persist-credentials: false - name: Install Rust toolchain - # Only the minimal profile is required here to run `cargo` - run: rustup toolchain install stable --profile minimal + run: rustup toolchain install - - name: Install pnpm - uses: pnpm/action-setup@v6.0.9 - - - name: Install Node - uses: actions/setup-node@v7.0.0 + - name: Install pnpm, Node.js and the dependencies + uses: pnpm/setup@703c52620218391530e48b9e8870d5c0082e1b9b # v2.1.0 with: - node-version-file: .node-version - cache: "pnpm" - - - name: Install dependencies - run: pnpm install --frozen-lockfile + cache: true + require-lockfile: true - name: Extract the current version id: current diff --git a/.github/workflows/tag.yaml b/.github/workflows/tag.yaml index fba71867d..c52fa107d 100644 --- a/.github/workflows/tag.yaml +++ b/.github/workflows/tag.yaml @@ -1,3 +1,4 @@ +# Copyright 2025, 2026 Element Creations Ltd. # Copyright 2025 New Vector Ltd. # # SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial @@ -35,8 +36,7 @@ jobs: persist-credentials: false - name: Install Rust toolchain - # Only the minimal profile is required here to run `cargo metadata` - run: rustup toolchain install stable --profile minimal + run: rustup toolchain install - name: Set the crates version env: diff --git a/.github/workflows/translations-download.yaml b/.github/workflows/translations-download.yaml index 957781591..f106b4c47 100644 --- a/.github/workflows/translations-download.yaml +++ b/.github/workflows/translations-download.yaml @@ -1,3 +1,4 @@ +# Copyright 2025, 2026 Element Creations Ltd. # Copyright 2025 New Vector Ltd. # # SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial @@ -23,17 +24,11 @@ jobs: with: persist-credentials: false - - name: Install pnpm - uses: pnpm/action-setup@v6.0.9 - - - name: Install Node - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + - name: Install pnpm, Node.js and the dependencies + uses: pnpm/setup@703c52620218391530e48b9e8870d5c0082e1b9b # v2.1.0 with: - node-version-file: .node-version - cache: "pnpm" - - - name: Install dependencies - run: pnpm install --frozen-lockfile + cache: true + require-lockfile: true - name: Compute the Localazy branch name id: branch diff --git a/.github/workflows/translations-upload.yaml b/.github/workflows/translations-upload.yaml index d81b04074..08d8d8974 100644 --- a/.github/workflows/translations-upload.yaml +++ b/.github/workflows/translations-upload.yaml @@ -1,3 +1,4 @@ +# Copyright 2025, 2026 Element Creations Ltd. # Copyright 2025 New Vector Ltd. # # SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial @@ -22,17 +23,11 @@ jobs: with: persist-credentials: false - - name: Install pnpm - uses: pnpm/action-setup@v6.0.9 - - - name: Install Node - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + - name: Install pnpm, Node.js and the dependencies + uses: pnpm/setup@703c52620218391530e48b9e8870d5c0082e1b9b # v2.1.0 with: - node-version-file: .node-version - cache: "pnpm" - - - name: Install dependencies - run: pnpm install --frozen-lockfile + cache: true + require-lockfile: true - name: Compute the Localazy branch name id: branch diff --git a/.rustfmt.toml b/.rustfmt.toml index 72a97f569..aee976484 100644 --- a/.rustfmt.toml +++ b/.rustfmt.toml @@ -1,11 +1,8 @@ +# Copyright 2025, 2026 Element Creations Ltd. # Copyright 2025 New Vector Ltd. # # SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial # Please see LICENSE files in the repository root for full details. -max_width = 100 -comment_width = 80 -wrap_comments = true imports_granularity = "Crate" -use_small_heuristics = "Default" group_imports = "StdExternalCrate" diff --git a/Cargo.lock b/Cargo.lock index 07924b34f..fea904a2d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1747,7 +1747,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -2127,9 +2127,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.15" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155" +checksum = "a9f37a958b41b3b19ee2707c06439c0e9e547e847223eb791ecb0cb821c65e27" dependencies = [ "atomic-waker", "bytes", @@ -2304,9 +2304,9 @@ dependencies = [ [[package]] name = "http-body-util" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +checksum = "23169fe34a5fbcdd3f3862e78fb9b6fccd5f02a6dc6f732547005d45631ce71c" dependencies = [ "bytes", "futures-core", @@ -3198,7 +3198,7 @@ dependencies = [ [[package]] name = "mas-axum-utils" -version = "1.22.0" +version = "1.24.0" dependencies = [ "anyhow", "axum", @@ -3233,7 +3233,7 @@ dependencies = [ [[package]] name = "mas-cli" -version = "1.22.0" +version = "1.24.0" dependencies = [ "anyhow", "axum", @@ -3293,6 +3293,7 @@ dependencies = [ "serde_yaml", "sqlx", "syn2mas", + "tempfile", "tokio", "tokio-util", "tower", @@ -3308,7 +3309,7 @@ dependencies = [ [[package]] name = "mas-config" -version = "1.22.0" +version = "1.24.0" dependencies = [ "anyhow", "camino", @@ -3339,7 +3340,7 @@ dependencies = [ [[package]] name = "mas-context" -version = "1.22.0" +version = "1.24.0" dependencies = [ "console", "opentelemetry", @@ -3356,7 +3357,7 @@ dependencies = [ [[package]] name = "mas-data-model" -version = "1.22.0" +version = "1.24.0" dependencies = [ "base64ct", "chrono", @@ -3378,7 +3379,7 @@ dependencies = [ [[package]] name = "mas-email" -version = "1.22.0" +version = "1.24.0" dependencies = [ "async-trait", "lettre", @@ -3389,7 +3390,7 @@ dependencies = [ [[package]] name = "mas-handlers" -version = "1.22.0" +version = "1.24.0" dependencies = [ "aide", "anyhow", @@ -3472,7 +3473,7 @@ dependencies = [ [[package]] name = "mas-http" -version = "1.22.0" +version = "1.24.0" dependencies = [ "futures-util", "headers", @@ -3492,7 +3493,7 @@ dependencies = [ [[package]] name = "mas-i18n" -version = "1.22.0" +version = "1.24.0" dependencies = [ "camino", "icu_datetime", @@ -3512,7 +3513,7 @@ dependencies = [ [[package]] name = "mas-i18n-scan" -version = "1.22.0" +version = "1.24.0" dependencies = [ "camino", "clap", @@ -3526,7 +3527,7 @@ dependencies = [ [[package]] name = "mas-iana" -version = "1.22.0" +version = "1.24.0" dependencies = [ "schemars 1.2.1", "serde", @@ -3534,7 +3535,7 @@ dependencies = [ [[package]] name = "mas-iana-codegen" -version = "1.22.0" +version = "1.24.0" dependencies = [ "anyhow", "async-trait", @@ -3551,7 +3552,7 @@ dependencies = [ [[package]] name = "mas-jose" -version = "1.22.0" +version = "1.24.0" dependencies = [ "base64ct", "chrono", @@ -3581,7 +3582,7 @@ dependencies = [ [[package]] name = "mas-keystore" -version = "1.22.0" +version = "1.24.0" dependencies = [ "aead", "base64ct", @@ -3609,7 +3610,7 @@ dependencies = [ [[package]] name = "mas-listener" -version = "1.22.0" +version = "1.24.0" dependencies = [ "anyhow", "bytes", @@ -3633,7 +3634,7 @@ dependencies = [ [[package]] name = "mas-matrix" -version = "1.22.0" +version = "1.24.0" dependencies = [ "anyhow", "async-trait", @@ -3643,7 +3644,7 @@ dependencies = [ [[package]] name = "mas-matrix-synapse" -version = "1.22.0" +version = "1.24.0" dependencies = [ "anyhow", "async-trait", @@ -3660,7 +3661,7 @@ dependencies = [ [[package]] name = "mas-oidc-client" -version = "1.22.0" +version = "1.24.0" dependencies = [ "assert_matches", "async-trait", @@ -3673,6 +3674,7 @@ dependencies = [ "http", "http-body-util", "language-tags", + "mas-data-model", "mas-http", "mas-iana", "mas-jose", @@ -3696,7 +3698,7 @@ dependencies = [ [[package]] name = "mas-policy" -version = "1.22.0" +version = "1.24.0" dependencies = [ "anyhow", "arc-swap", @@ -3713,7 +3715,7 @@ dependencies = [ [[package]] name = "mas-router" -version = "1.22.0" +version = "1.24.0" dependencies = [ "axum", "serde", @@ -3724,7 +3726,7 @@ dependencies = [ [[package]] name = "mas-spa" -version = "1.22.0" +version = "1.24.0" dependencies = [ "camino", "serde", @@ -3733,7 +3735,7 @@ dependencies = [ [[package]] name = "mas-storage" -version = "1.22.0" +version = "1.24.0" dependencies = [ "async-trait", "chrono", @@ -3755,7 +3757,7 @@ dependencies = [ [[package]] name = "mas-storage-pg" -version = "1.22.0" +version = "1.24.0" dependencies = [ "async-trait", "chrono", @@ -3787,7 +3789,7 @@ dependencies = [ [[package]] name = "mas-tasks" -version = "1.22.0" +version = "1.24.0" dependencies = [ "anyhow", "async-trait", @@ -3819,7 +3821,7 @@ dependencies = [ [[package]] name = "mas-templates" -version = "1.22.0" +version = "1.24.0" dependencies = [ "anyhow", "arc-swap", @@ -3851,7 +3853,7 @@ dependencies = [ [[package]] name = "mas-tower" -version = "1.22.0" +version = "1.24.0" dependencies = [ "http", "opentelemetry", @@ -4103,7 +4105,7 @@ dependencies = [ [[package]] name = "oauth2-types" -version = "1.22.0" +version = "1.24.0" dependencies = [ "assert_matches", "base64ct", @@ -5287,7 +5289,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -5345,7 +5347,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -5958,7 +5960,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -6279,7 +6281,7 @@ dependencies = [ [[package]] name = "syn2mas" -version = "1.22.0" +version = "1.24.0" dependencies = [ "anyhow", "arc-swap", @@ -6371,7 +6373,7 @@ dependencies = [ "getrandom 0.3.3", "once_cell", "rustix", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -6804,6 +6806,7 @@ version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" dependencies = [ + "chrono", "matchers", "nu-ansi-term", "once_cell", @@ -7548,7 +7551,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.48.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 377edbfad..1cec30dea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ members = ["crates/*"] resolver = "2" # Updated in the CI with a `sed` command -package.version = "1.22.0" +package.version = "1.24.0" package.license = "AGPL-3.0-only OR LicenseRef-Element-Commercial" package.authors = ["Element Backend Team"] package.edition = "2024" @@ -42,35 +42,35 @@ broken_intra_doc_links = "deny" [workspace.dependencies] # Workspace crates -mas-axum-utils = { path = "./crates/axum-utils/", version = "=1.22.0" } -mas-cli = { path = "./crates/cli/", version = "=1.22.0" } -mas-config = { path = "./crates/config/", version = "=1.22.0" } -mas-context = { path = "./crates/context/", version = "=1.22.0" } -mas-data-model = { path = "./crates/data-model/", version = "=1.22.0" } -mas-email = { path = "./crates/email/", version = "=1.22.0" } -mas-graphql = { path = "./crates/graphql/", version = "=1.22.0" } -mas-handlers = { path = "./crates/handlers/", version = "=1.22.0" } -mas-http = { path = "./crates/http/", version = "=1.22.0" } -mas-i18n = { path = "./crates/i18n/", version = "=1.22.0" } -mas-i18n-scan = { path = "./crates/i18n-scan/", version = "=1.22.0" } -mas-iana = { path = "./crates/iana/", version = "=1.22.0" } -mas-iana-codegen = { path = "./crates/iana-codegen/", version = "=1.22.0" } -mas-jose = { path = "./crates/jose/", version = "=1.22.0" } -mas-keystore = { path = "./crates/keystore/", version = "=1.22.0" } -mas-listener = { path = "./crates/listener/", version = "=1.22.0" } -mas-matrix = { path = "./crates/matrix/", version = "=1.22.0" } -mas-matrix-synapse = { path = "./crates/matrix-synapse/", version = "=1.22.0" } -mas-oidc-client = { path = "./crates/oidc-client/", version = "=1.22.0" } -mas-policy = { path = "./crates/policy/", version = "=1.22.0" } -mas-router = { path = "./crates/router/", version = "=1.22.0" } -mas-spa = { path = "./crates/spa/", version = "=1.22.0" } -mas-storage = { path = "./crates/storage/", version = "=1.22.0" } -mas-storage-pg = { path = "./crates/storage-pg/", version = "=1.22.0" } -mas-tasks = { path = "./crates/tasks/", version = "=1.22.0" } -mas-templates = { path = "./crates/templates/", version = "=1.22.0" } -mas-tower = { path = "./crates/tower/", version = "=1.22.0" } -oauth2-types = { path = "./crates/oauth2-types/", version = "=1.22.0" } -syn2mas = { path = "./crates/syn2mas", version = "=1.22.0" } +mas-axum-utils = { path = "./crates/axum-utils/", version = "=1.24.0" } +mas-cli = { path = "./crates/cli/", version = "=1.24.0" } +mas-config = { path = "./crates/config/", version = "=1.24.0" } +mas-context = { path = "./crates/context/", version = "=1.24.0" } +mas-data-model = { path = "./crates/data-model/", version = "=1.24.0" } +mas-email = { path = "./crates/email/", version = "=1.24.0" } +mas-graphql = { path = "./crates/graphql/", version = "=1.24.0" } +mas-handlers = { path = "./crates/handlers/", version = "=1.24.0" } +mas-http = { path = "./crates/http/", version = "=1.24.0" } +mas-i18n = { path = "./crates/i18n/", version = "=1.24.0" } +mas-i18n-scan = { path = "./crates/i18n-scan/", version = "=1.24.0" } +mas-iana = { path = "./crates/iana/", version = "=1.24.0" } +mas-iana-codegen = { path = "./crates/iana-codegen/", version = "=1.24.0" } +mas-jose = { path = "./crates/jose/", version = "=1.24.0" } +mas-keystore = { path = "./crates/keystore/", version = "=1.24.0" } +mas-listener = { path = "./crates/listener/", version = "=1.24.0" } +mas-matrix = { path = "./crates/matrix/", version = "=1.24.0" } +mas-matrix-synapse = { path = "./crates/matrix-synapse/", version = "=1.24.0" } +mas-oidc-client = { path = "./crates/oidc-client/", version = "=1.24.0" } +mas-policy = { path = "./crates/policy/", version = "=1.24.0" } +mas-router = { path = "./crates/router/", version = "=1.24.0" } +mas-spa = { path = "./crates/spa/", version = "=1.24.0" } +mas-storage = { path = "./crates/storage/", version = "=1.24.0" } +mas-storage-pg = { path = "./crates/storage-pg/", version = "=1.24.0" } +mas-tasks = { path = "./crates/tasks/", version = "=1.24.0" } +mas-templates = { path = "./crates/templates/", version = "=1.24.0" } +mas-tower = { path = "./crates/tower/", version = "=1.24.0" } +oauth2-types = { path = "./crates/oauth2-types/", version = "=1.24.0" } +syn2mas = { path = "./crates/syn2mas", version = "=1.24.0" } # OpenAPI schema generation and validation [workspace.dependencies.aide] @@ -688,7 +688,7 @@ features = ["cors", "fs", "add-extension", "set-header"] version = "0.1.41" [workspace.dependencies.tracing-subscriber] version = "0.3.23" -features = ["env-filter"] +features = ["env-filter", "chrono"] [workspace.dependencies.tracing-appender] version = "0.2.5" diff --git a/Dockerfile b/Dockerfile index b7e3b2bd2..0a644d6ed 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,9 +13,8 @@ # The Debian version and version name must be in sync ARG DEBIAN_VERSION=13 ARG DEBIAN_VERSION_NAME=trixie -# Keep in sync with .github/workflows/ci.yaml -ARG RUSTC_VERSION=1.96.0 -# Keep in sync with .node-version +ARG RUSTUP_VERSION=1.29.0 +# Keep in sync with .node-version and devEngines.runtime in package.json ARG NODEJS_VERSION=24.15.0 # Keep in sync with .github/actions/build-policies/action.yml and policies/Makefile ARG OPA_VERSION=1.13.1 @@ -39,8 +38,10 @@ COPY ./package.json ./pnpm-workspace.yaml ./pnpm-lock.yaml /app/ COPY ./frontend/package.json /app/frontend/ # Network access: to fetch dependencies +# The base image already provides the Node.js version pinned in +# `devEngines.runtime`, so skip pnpm's own runtime download. RUN --network=default \ - pnpm install --frozen-lockfile + pnpm install --frozen-lockfile --no-runtime COPY ./frontend/ /app/frontend/ COPY ./templates/ /app/templates/ @@ -76,10 +77,42 @@ RUN --network=none \ ######################################## ## Build stage that builds the binary ## ######################################## -FROM --platform=${BUILDPLATFORM} docker.io/library/rust:${RUSTC_VERSION}-${DEBIAN_VERSION_NAME} AS builder +FROM --platform=${BUILDPLATFORM} docker.io/library/buildpack-deps:${DEBIAN_VERSION_NAME} AS builder +ARG BUILDARCH ARG CARGO_AUDITABLE_VERSION -ARG RUSTC_VERSION +ARG RUSTUP_VERSION + +ENV RUSTUP_HOME=/usr/local/rustup \ + CARGO_HOME=/usr/local/cargo \ + PATH=/usr/local/cargo/bin:$PATH + +# Checksums come from https://static.rust-lang.org/rustup/archive///rustup-init.sha256 +# Network access: to download rustup +RUN --network=default \ + case "${BUILDARCH}" in \ + amd64) RUSTUP_TRIPLE="x86_64-unknown-linux-gnu"; RUSTUP_SHA256="4acc9acc76d5079515b46346a485974457b5a79893cfb01112423c89aeb5aa10" ;; \ + arm64) RUSTUP_TRIPLE="aarch64-unknown-linux-gnu"; RUSTUP_SHA256="9732d6c5e2a098d3521fca8145d826ae0aaa067ef2385ead08e6feac88fa5792" ;; \ + *) echo "unsupported architecture: ${BUILDARCH}" >&2; exit 1 ;; \ + esac && \ + curl -fsSLo rustup-init "https://static.rust-lang.org/rustup/archive/${RUSTUP_VERSION}/${RUSTUP_TRIPLE}/rustup-init" && \ + echo "${RUSTUP_SHA256} *rustup-init" | sha256sum -c - && \ + chmod +x rustup-init && \ + ./rustup-init -y --no-modify-path --default-toolchain none && \ + rm rustup-init + +# Set the working directory +WORKDIR /app + +# Copied on its own so the toolchain layer is cached independently of the source tree +COPY rust-toolchain.toml ./ + +# Network access: to download the toolchain and the cross-compilation targets +RUN --network=default \ + rustup toolchain install && \ + rustup target add \ + x86_64-unknown-linux-gnu \ + aarch64-unknown-linux-gnu # Install pinned versions of cargo-auditable # Network access: to fetch dependencies @@ -87,14 +120,6 @@ RUN --network=default \ cargo install --locked \ cargo-auditable@=${CARGO_AUDITABLE_VERSION} -# Install all cross-compilation targets -# Network access: to download the targets -RUN --network=default \ - rustup target add \ - --toolchain "${RUSTC_VERSION}" \ - x86_64-unknown-linux-gnu \ - aarch64-unknown-linux-gnu - RUN --network=none \ dpkg --add-architecture arm64 && \ dpkg --add-architecture amd64 @@ -120,9 +145,6 @@ ENV \ CC_x86_64_unknown_linux_gnu=x86_64-linux-gnu-gcc \ CXX_x86_64_unknown_linux_gnu=x86_64-linux-gnu-g++ -# Set the working directory -WORKDIR /app - # Copy the code COPY ./ /app ENV SQLX_OFFLINE=true @@ -134,7 +156,7 @@ ARG TARGETARCH # Network access: cargo auditable needs it RUN --network=default \ - --mount=type=cache,target=/root/.cargo/registry \ + --mount=type=cache,target=/usr/local/cargo/registry \ --mount=type=cache,target=/app/target \ RUST_TARGET=$(case "${TARGETARCH}" in \ amd64) echo "x86_64-unknown-linux-gnu" ;; \ diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index 5341a15f9..fae239425 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -91,6 +91,9 @@ mas-tower.workspace = true syn2mas.workspace = true +[dev-dependencies] +tempfile = "3" + [build-dependencies] anyhow.workspace = true vergen-gitcl.workspace = true diff --git a/crates/cli/src/lifecycle.rs b/crates/cli/src/lifecycle.rs index 3da49f9d1..0f551c0bf 100644 --- a/crates/cli/src/lifecycle.rs +++ b/crates/cli/src/lifecycle.rs @@ -217,11 +217,14 @@ impl LifecycleManager { self.hard_shutdown_token().cancel(); - // TODO: we may want to have a time out on the task tracker, in case we have - // really stuck tasks on it - self.task_tracker().wait().await; - - tracing::info!("All tasks are done, exitting"); + tokio::select! { + () = self.task_tracker().wait() => { + tracing::info!("All tasks are done, exiting"); + }, + () = tokio::time::sleep(self.timeout) => { + tracing::warn!("Hard shutdown timeout reached with tasks still running, exiting anyway"); + }, + } if likely_crashed { ExitCode::FAILURE diff --git a/crates/cli/src/util.rs b/crates/cli/src/util.rs index c9832ef79..5172ae1ed 100644 --- a/crates/cli/src/util.rs +++ b/crates/cli/src/util.rs @@ -627,4 +627,61 @@ mod tests { let manager = password_manager_from_config(&config).await; assert!(manager.is_err()); } + + /// RAII guard that removes an environment variable when dropped, + /// ensuring cleanup even if the test panics. + struct EnvVarGuard(&'static str); + + impl EnvVarGuard { + fn set(key: &'static str, value: impl AsRef) -> Self { + // SAFETY: single-threaded tokio test runtime; no other thread + // is reading or writing the environment concurrently. + #[expect(unsafe_code)] + unsafe { + std::env::set_var(key, value); + } + Self(key) + } + } + + impl Drop for EnvVarGuard { + fn drop(&mut self) { + // SAFETY: single-threaded tokio test runtime; no other thread + // is reading or writing the environment concurrently. + #[expect(unsafe_code)] + unsafe { + std::env::remove_var(self.0); + } + } + } + + #[tokio::test] + async fn test_database_connection_with_pgpass() { + // Write a temporary pgpass file and point PGPASSFILE at it + let pgpass_content = "*:*:*:testuser:testpassword\n"; + let pgpass_file = tempfile::NamedTempFile::new().expect("failed to create temp file"); + tokio::fs::write(pgpass_file.path(), pgpass_content) + .await + .expect("failed to write pgpass file"); + + // Set PGPASSFILE for sqlx to pick up the password from the pgpass file + let _guard = EnvVarGuard::set("PGPASSFILE", pgpass_file.path()); + + let config = serde_json::from_value(serde_json::json!({ + "uri": "postgresql://testuser@localhost/test" + })) + .unwrap(); + + let opts = DatabaseConnectOptions { + log_slow_statements: false, + }; + + let result = database_connect_options_from_config(&config, &opts).await; + assert!(result.is_ok()); + let debug = format!("{:?}", result.unwrap()); + assert!( + debug.contains("testpassword"), + "pgpass password was not resolved: {debug}" + ); + } } diff --git a/crates/context/src/fmt.rs b/crates/context/src/fmt.rs index 0b6edb130..3be0ed3b2 100644 --- a/crates/context/src/fmt.rs +++ b/crates/context/src/fmt.rs @@ -4,6 +4,8 @@ // SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial // Please see LICENSE files in the repository root for full details. +use std::sync::LazyLock; + use console::{Color, Style}; use opentelemetry::{TraceId, trace::TraceContextExt as _}; use tracing::{Level, Subscriber}; @@ -12,13 +14,16 @@ use tracing_subscriber::{ fmt::{ FormatEvent, FormatFields, format::{DefaultFields, Writer}, - time::{FormatTime, SystemTime}, + time::{ChronoLocal, FormatTime}, }, registry::LookupSpan, }; use crate::LogContext; +static TIMER: LazyLock = + LazyLock::new(|| ChronoLocal::new("%Y-%m-%dT%H:%M:%S%.6f%:z".to_owned())); + /// An event formatter usable by the [`tracing_subscriber`] crate, which /// includes the log context and the OTEL trace ID. #[derive(Debug, Default)] @@ -98,7 +103,7 @@ where let ansi = writer.has_ansi_escapes(); let metadata = event.metadata(); - SystemTime.format_time(&mut writer)?; + TIMER.format_time(&mut writer)?; let level = FmtLevel::new(metadata.level(), ansi); write!(&mut writer, " {level} ")?; diff --git a/crates/handlers/src/admin/v1/users/add.rs b/crates/handlers/src/admin/v1/users/add.rs index 976ae818f..48b357043 100644 --- a/crates/handlers/src/admin/v1/users/add.rs +++ b/crates/handlers/src/admin/v1/users/add.rs @@ -125,13 +125,9 @@ pub fn doc(operation: TransformOperation) -> TransformOperation { let response = ErrorResponse::from_error(&RouteError::UsernameNotValid); t.description("Username is not valid").example(response) }) - .response_with::<409, RouteError, _>(|t| { - let response = ErrorResponse::from_error(&RouteError::UserAlreadyExists); - t.description("User already exists").example(response) - }) .response_with::<409, RouteError, _>(|t| { let response = ErrorResponse::from_error(&RouteError::UsernameReserved); - t.description("Username is reserved by the homeserver") + t.description("User already exists or the username is reserved by the homeserver") .example(response) }) } diff --git a/crates/handlers/src/compat/login.rs b/crates/handlers/src/compat/login.rs index efa8358ae..f30b1bab4 100644 --- a/crates/handlers/src/compat/login.rs +++ b/crates/handlers/src/compat/login.rs @@ -155,6 +155,9 @@ pub enum Credentials { #[serde(rename = "m.login.token")] Token { token: String }, + #[serde(rename = "m.login.application_service")] + ApplicationService, + #[serde(other)] Unsupported, } @@ -164,6 +167,7 @@ impl Credentials { match self { Self::Password { .. } => "m.login.password", Self::Token { .. } => "m.login.token", + Self::ApplicationService => "m.login.application_service", Self::Unsupported => "unsupported", } } @@ -199,6 +203,9 @@ pub enum RouteError { #[error("unsupported login method")] Unsupported, + #[error("appservice login not supported")] + AppserviceLoginUnsupported, + #[error("unsupported identifier type")] UnsupportedIdentifier, @@ -266,6 +273,11 @@ impl IntoResponse for RouteError { error: "Invalid login type", status: StatusCode::BAD_REQUEST, }, + Self::AppserviceLoginUnsupported => MatrixError { + errcode: "M_APPSERVICE_LOGIN_UNSUPPORTED", + error: "Application services can't log in through the legacy authentication API on this server", + status: StatusCode::BAD_REQUEST, + }, Self::UnsupportedIdentifier => MatrixError { errcode: "M_UNKNOWN", error: "Unsupported login identifier", @@ -394,6 +406,10 @@ pub(crate) async fn post( .await? } + (_, Credentials::ApplicationService) => { + return Err(RouteError::AppserviceLoginUnsupported); + } + _ => { return Err(RouteError::Unsupported); } @@ -1686,6 +1702,31 @@ mod tests { "###); } + /// Test the response of an `m.login.application_service` login. + #[sqlx::test(migrator = "mas_storage_pg::MIGRATOR")] + async fn test_appservice_login_unsupported(pool: PgPool) { + setup(); + let state = TestState::from_pool(pool).await.unwrap(); + + let request = Request::post("/_matrix/client/v3/login").json(serde_json::json!({ + "type": "m.login.application_service", + "identifier": { + "type": "m.id.user", + "user": "_irc_example", + }, + })); + + let response = state.request(request).await; + response.assert_status(StatusCode::BAD_REQUEST); + let body: serde_json::Value = response.json(); + insta::assert_json_snapshot!(body, @r###" + { + "errcode": "M_APPSERVICE_LOGIN_UNSUPPORTED", + "error": "Application services can't log in through the legacy authentication API on this server" + } + "###); + } + /// Test `m.login.token` login flow. #[sqlx::test(migrator = "mas_storage_pg::MIGRATOR")] async fn test_login_token_login(pool: PgPool) { diff --git a/crates/oidc-client/Cargo.toml b/crates/oidc-client/Cargo.toml index 3fffd584c..ec1b1eb89 100644 --- a/crates/oidc-client/Cargo.toml +++ b/crates/oidc-client/Cargo.toml @@ -51,3 +51,5 @@ rand_chacha.workspace = true rustls.workspace = true tokio.workspace = true wiremock.workspace = true + +mas-data-model.workspace = true diff --git a/crates/oidc-client/tests/it/main.rs b/crates/oidc-client/tests/it/main.rs index f05340467..28960e9d5 100644 --- a/crates/oidc-client/tests/it/main.rs +++ b/crates/oidc-client/tests/it/main.rs @@ -7,6 +7,7 @@ use std::collections::HashMap; use chrono::{DateTime, Duration, Utc}; +use mas_data_model::{Clock, clock::MockClock}; use mas_iana::{jose::JsonWebSignatureAlg, oauth::OAuthClientAuthenticationMethod}; use mas_jose::{ claims::{self, hash_token}, @@ -37,9 +38,13 @@ const REFRESH_TOKEN: &str = "RefreshToken1"; const SUBJECT_IDENTIFIER: &str = "SubjectID"; const ID_TOKEN_SIGNING_ALG: JsonWebSignatureAlg = JsonWebSignatureAlg::Rs256; +/// The current time, as seen by every test in this crate. +/// +/// Tests never read the system clock: they use a [`MockClock`] frozen at a +/// fixed instant, so token timestamps and verification always agree no matter +/// how long the setup in between takes. fn now() -> DateTime { - #[expect(clippy::disallowed_methods)] - Utc::now() + MockClock::default().now() } async fn init_test() -> (reqwest::Client, MockServer, Url) { diff --git a/crates/templates/src/context.rs b/crates/templates/src/context.rs index 98121a9ad..090ba4333 100644 --- a/crates/templates/src/context.rs +++ b/crates/templates/src/context.rs @@ -32,7 +32,10 @@ use mas_i18n::DataLocale; use mas_iana::jose::JsonWebSignatureAlg; use mas_policy::{Violation, ViolationVariant}; use mas_router::{Account, GraphQL, PostAuthAction, UrlBuilder}; -use oauth2_types::scope::{OPENID, Scope}; +use oauth2_types::{ + requests::ResponseMode, + scope::{OPENID, Scope}, +}; use rand::{ Rng, SeedableRng, distributions::{Alphanumeric, DistString}, @@ -750,20 +753,32 @@ impl TemplateContext for ConsentContext { sample_list( Client::samples(now, rng) .into_iter() - .map(|client| { - let mut grant = AuthorizationGrant::sample(now, rng); - let action = PostAuthAction::continue_grant(grant.id); - // XXX - grant.client_id = client.id; - Self { - grant, - client, - action, - matrix_user: MatrixUser { - mxid: "@alice:example.com".to_owned(), - display_name: Some("Alice".to_owned()), - }, - } + .flat_map(|client| { + [ + (None, ResponseMode::Query), + (None, ResponseMode::Fragment), + (None, ResponseMode::FormPost), + (Some("some-state".to_owned()), ResponseMode::Query), + (Some("some-state".to_owned()), ResponseMode::Fragment), + (Some("some-state".to_owned()), ResponseMode::FormPost), + ] + .map(|(state, response_mode)| { + let mut grant = AuthorizationGrant::sample(now, rng); + let action = PostAuthAction::continue_grant(grant.id); + // XXX + grant.client_id = client.id; + grant.state = state; + grant.response_mode = response_mode; + Self { + grant, + client: client.clone(), + action, + matrix_user: MatrixUser { + mxid: "@alice:example.com".to_owned(), + display_name: Some("Alice".to_owned()), + }, + } + }) }) .collect(), ) @@ -820,10 +835,22 @@ impl TemplateContext for PolicyViolationContext { grant.client_id = client.id; let authorization_grant = PolicyViolationContext::for_authorization_grant( - grant, + grant.clone(), client.clone(), Vec::new(), ); + + let authorization_grant_invalid_scope = + PolicyViolationContext::for_authorization_grant( + grant, + client.clone(), + vec![Violation { + msg: "scope 'foo' not allowed".to_owned(), + redirect_uri: None, + field: None, + variant: None, + }], + ); let device_code_grant = PolicyViolationContext::for_device_code_grant( DeviceCodeGrant { id: Ulid::from_datetime_with_rng(now, rng), @@ -838,11 +865,42 @@ impl TemplateContext for PolicyViolationContext { user_agent: None, locale: None, }, - client, + client.clone(), Vec::new(), ); - [authorization_grant, device_code_grant] + let device_code_grant_invalid_scope = + PolicyViolationContext::for_device_code_grant( + DeviceCodeGrant { + id: Ulid::from_datetime_with_rng(now, rng), + state: mas_data_model::DeviceCodeGrantState::Pending, + client_id: client.id, + scope: [OPENID].into_iter().collect(), + user_code: Alphanumeric.sample_string(rng, 6).to_uppercase(), + device_code: Alphanumeric.sample_string(rng, 32), + created_at: now - Duration::try_minutes(5).unwrap(), + expires_at: now + Duration::try_minutes(25).unwrap(), + ip_address: None, + user_agent: None, + locale: None, + }, + client, + vec![Violation { + msg: "user has too many active sessions".to_owned(), + redirect_uri: None, + field: None, + variant: Some(ViolationVariant::TooManySessions { + need_to_remove: 1, + }), + }], + ); + + [ + authorization_grant, + authorization_grant_invalid_scope, + device_code_grant, + device_code_grant_invalid_scope, + ] }) .collect(), ) @@ -902,6 +960,14 @@ impl TemplateContext for CompatLoginPolicyViolationContext { { sample_list(vec![ CompatLoginPolicyViolationContext { violations: vec![] }, + CompatLoginPolicyViolationContext { + violations: vec![Violation { + msg: "scope 'foo' not allowed".to_owned(), + redirect_uri: None, + field: None, + variant: None, + }], + }, CompatLoginPolicyViolationContext { violations: vec![Violation { msg: "user has too many active sessions".to_owned(), diff --git a/crates/templates/src/functions.rs b/crates/templates/src/functions.rs index 16515572e..05bc0f313 100644 --- a/crates/templates/src/functions.rs +++ b/crates/templates/src/functions.rs @@ -195,7 +195,13 @@ fn function_add_params_to_url( // Merge the exising and the additional parameters together // Use a BTreeMap for determinism (because it orders keys) - let params: BTreeMap<&String, &Value> = params.iter().chain(existing.iter()).collect(); + // Parameters which have no value (e.g. an absent `state`) are skipped, as they + // can't be serialized back to a query string + let params: BTreeMap<&String, &Value> = params + .iter() + .chain(existing.iter()) + .filter(|(_key, value)| !value.is_none() && !value.is_undefined()) + .collect(); // Transform them back to urlencoded let params = serde_urlencoded::to_string(params).map_err(|e| { diff --git a/deny.toml b/deny.toml index e0fd70fc9..7d6a0cf7f 100644 --- a/deny.toml +++ b/deny.toml @@ -27,8 +27,15 @@ ignore = [ # Engine/Store, and as the advisory says explicitly: # > This bug is not triggerable by guest WebAssembly programs. "RUSTSEC-2026-0222", + # A bug in wasmtime, but we are not affected as we're not using the WASI + # capabilities and filesystem APIs + "RUSTSEC-2026-0269", ] +# Only warn about unmaintained crates in direct dependencies of the workspace, +# not transitive dependencies +unmaintained = "workspace" + [licenses] version = 2 allow = [ diff --git a/docs/api/spec.json b/docs/api/spec.json index 72c4bf0e2..8f4cbbce4 100644 --- a/docs/api/spec.json +++ b/docs/api/spec.json @@ -2310,7 +2310,7 @@ } }, "409": { - "description": "Username is reserved by the homeserver", + "description": "User already exists or the username is reserved by the homeserver", "content": { "application/json": { "schema": { diff --git a/docs/as-login.md b/docs/as-login.md index 0eed4f9e3..f3b61c257 100644 --- a/docs/as-login.md +++ b/docs/as-login.md @@ -1,7 +1,7 @@ # About Application Services login Encrypted Application Services/Bridges currently leverage the `m.login.application_service` login type to create devices for users. -This API is *not* available in the Matrix Authentication Service. +This API is *not* available in the Matrix Authentication Service: as per [Matrix 1.19](https://spec.matrix.org/v1.19/application-service-api/#registration), calling `/login` with this login type returns a 400 HTTP status code with an `M_APPSERVICE_LOGIN_UNSUPPORTED` error code. We're working on a solution to support this use case, but in the meantime, this means **encrypted bridges will not work with the Matrix Authentication Service.** A workaround is to disable E2EE support in your bridge setup. diff --git a/docs/development/contributing.md b/docs/development/contributing.md index e0c962f83..a9079b17e 100644 --- a/docs/development/contributing.md +++ b/docs/development/contributing.md @@ -19,8 +19,8 @@ There are two main ways to contribute to MAS: To get MAS running locally from source you will need to: -- [Install Rust and Cargo](https://www.rust-lang.org/learn/get-started). We recommend using the latest stable version of Rust. -- [Install Node.js](https://nodejs.org/). We recommend using the latest LTS version of Node.js. The frontend uses pnpm, which is installed automatically via corepack — see below. +- [Install Rust and Cargo](https://www.rust-lang.org/learn/get-started). The exact version is pinned in `rust-toolchain.toml`; rustup installs it automatically when you run cargo in the repo. +- [Install Node.js](https://nodejs.org/). Any recent version works to bootstrap: the exact version is pinned in `devEngines.runtime` in `package.json`, and pnpm downloads it on install. The frontend uses pnpm, which is installed automatically via corepack — see below. - [Install Open Policy Agent](https://www.openpolicyagent.org/docs#1-download-opa) ## 4. Get the source @@ -79,7 +79,7 @@ Most of them can be updated by running `sh misc/update.sh` at the root of the pr Make sure your code adheres to our Rust and TypeScript code style by running: - - `cargo +nightly fmt` (with the nightly toolchain installed) + - `cargo +nightly fmt` (`.rustfmt.toml` uses nightly-only options, so this needs the nightly toolchain installed) - `pnpm run format` in the `frontend` directory - `make fmt` in the `policies` directory (if changed) diff --git a/docs/setup/installation.md b/docs/setup/installation.md index f444f1bdf..9d92a24ee 100644 --- a/docs/setup/installation.md +++ b/docs/setup/installation.md @@ -63,8 +63,8 @@ The image can also be built from the source: Building from the source requires: -- The latest stable [Rust toolchain](https://www.rust-lang.org/learn/get-started) -- [Node.js (24 and later)](https://nodejs.org/en/), with [corepack](https://nodejs.org/api/corepack.html) enabled so pnpm@11 is provisioned automatically +- The [Rust toolchain](https://www.rust-lang.org/learn/get-started) pinned by `rust-toolchain.toml` (installed automatically by rustup) +- [Node.js](https://nodejs.org/en/), with [corepack](https://nodejs.org/api/corepack.html) enabled so pnpm@11 is provisioned automatically; pnpm then downloads the Node.js version pinned in `package.json` - the [Open Policy Agent](https://www.openpolicyagent.org/docs/latest/#running-opa) binary (or alternatively, Docker) 1. Get the source diff --git a/frontend/.storybook/locales.ts b/frontend/.storybook/locales.ts index 396dc18c5..deda65899 100644 --- a/frontend/.storybook/locales.ts +++ b/frontend/.storybook/locales.ts @@ -27,7 +27,7 @@ export type LocalazyMetadata = { }; const localazyMetadata: LocalazyMetadata = { - projectUrl: "https://localazy.com/p/matrix-authentication-service!v1.22", + projectUrl: "https://localazy.com/p/matrix-authentication-service!v1.24", baseLocale: "en", languages: [ { @@ -174,6 +174,15 @@ const localazyMetadata: LocalazyMetadata = { localizedName: "Svenska", pluralType: (n) => { return (n===1) ? "one" : "other"; } }, + { + language: "tr", + region: "", + script: "", + isRtl: false, + name: "Turkish", + localizedName: "Türkçe", + pluralType: (n) => { return (n===1) ? "one" : "other"; } + }, { language: "uk", region: "", @@ -208,25 +217,26 @@ const localazyMetadata: LocalazyMetadata = { file: "frontend.json", path: "", cdnFiles: { - "cs": "https://delivery.localazy.com/_a643710470041658449213cbf79a/_e0/7c203a8ac8bd48c3c4609a8effcd0fbac430f9b2/cs/frontend.json", - "da": "https://delivery.localazy.com/_a643710470041658449213cbf79a/_e0/7c203a8ac8bd48c3c4609a8effcd0fbac430f9b2/da/frontend.json", - "de": "https://delivery.localazy.com/_a643710470041658449213cbf79a/_e0/7c203a8ac8bd48c3c4609a8effcd0fbac430f9b2/de/frontend.json", - "en": "https://delivery.localazy.com/_a643710470041658449213cbf79a/_e0/7c203a8ac8bd48c3c4609a8effcd0fbac430f9b2/en/frontend.json", - "et": "https://delivery.localazy.com/_a643710470041658449213cbf79a/_e0/7c203a8ac8bd48c3c4609a8effcd0fbac430f9b2/et/frontend.json", - "fi": "https://delivery.localazy.com/_a643710470041658449213cbf79a/_e0/7c203a8ac8bd48c3c4609a8effcd0fbac430f9b2/fi/frontend.json", - "fr": "https://delivery.localazy.com/_a643710470041658449213cbf79a/_e0/7c203a8ac8bd48c3c4609a8effcd0fbac430f9b2/fr/frontend.json", - "hu": "https://delivery.localazy.com/_a643710470041658449213cbf79a/_e0/7c203a8ac8bd48c3c4609a8effcd0fbac430f9b2/hu/frontend.json", - "nb_NO": "https://delivery.localazy.com/_a643710470041658449213cbf79a/_e0/7c203a8ac8bd48c3c4609a8effcd0fbac430f9b2/nb-NO/frontend.json", - "nl": "https://delivery.localazy.com/_a643710470041658449213cbf79a/_e0/7c203a8ac8bd48c3c4609a8effcd0fbac430f9b2/nl/frontend.json", - "pl": "https://delivery.localazy.com/_a643710470041658449213cbf79a/_e0/7c203a8ac8bd48c3c4609a8effcd0fbac430f9b2/pl/frontend.json", - "pt": "https://delivery.localazy.com/_a643710470041658449213cbf79a/_e0/7c203a8ac8bd48c3c4609a8effcd0fbac430f9b2/pt/frontend.json", - "pt_BR": "https://delivery.localazy.com/_a643710470041658449213cbf79a/_e0/7c203a8ac8bd48c3c4609a8effcd0fbac430f9b2/pt-BR/frontend.json", - "ru": "https://delivery.localazy.com/_a643710470041658449213cbf79a/_e0/7c203a8ac8bd48c3c4609a8effcd0fbac430f9b2/ru/frontend.json", - "sk": "https://delivery.localazy.com/_a643710470041658449213cbf79a/_e0/7c203a8ac8bd48c3c4609a8effcd0fbac430f9b2/sk/frontend.json", - "sv": "https://delivery.localazy.com/_a643710470041658449213cbf79a/_e0/7c203a8ac8bd48c3c4609a8effcd0fbac430f9b2/sv/frontend.json", - "uk": "https://delivery.localazy.com/_a643710470041658449213cbf79a/_e0/7c203a8ac8bd48c3c4609a8effcd0fbac430f9b2/uk/frontend.json", - "uz": "https://delivery.localazy.com/_a643710470041658449213cbf79a/_e0/7c203a8ac8bd48c3c4609a8effcd0fbac430f9b2/uz/frontend.json", - "zh#Hans": "https://delivery.localazy.com/_a643710470041658449213cbf79a/_e0/7c203a8ac8bd48c3c4609a8effcd0fbac430f9b2/zh-Hans/frontend.json" + "cs": "https://delivery.localazy.com/_a6406816515547716152c4539eac/_e0/7c203a8ac8bd48c3c4609a8effcd0fbac430f9b2/cs/frontend.json", + "da": "https://delivery.localazy.com/_a6406816515547716152c4539eac/_e0/7c203a8ac8bd48c3c4609a8effcd0fbac430f9b2/da/frontend.json", + "de": "https://delivery.localazy.com/_a6406816515547716152c4539eac/_e0/7c203a8ac8bd48c3c4609a8effcd0fbac430f9b2/de/frontend.json", + "en": "https://delivery.localazy.com/_a6406816515547716152c4539eac/_e0/7c203a8ac8bd48c3c4609a8effcd0fbac430f9b2/en/frontend.json", + "et": "https://delivery.localazy.com/_a6406816515547716152c4539eac/_e0/7c203a8ac8bd48c3c4609a8effcd0fbac430f9b2/et/frontend.json", + "fi": "https://delivery.localazy.com/_a6406816515547716152c4539eac/_e0/7c203a8ac8bd48c3c4609a8effcd0fbac430f9b2/fi/frontend.json", + "fr": "https://delivery.localazy.com/_a6406816515547716152c4539eac/_e0/7c203a8ac8bd48c3c4609a8effcd0fbac430f9b2/fr/frontend.json", + "hu": "https://delivery.localazy.com/_a6406816515547716152c4539eac/_e0/7c203a8ac8bd48c3c4609a8effcd0fbac430f9b2/hu/frontend.json", + "nb_NO": "https://delivery.localazy.com/_a6406816515547716152c4539eac/_e0/7c203a8ac8bd48c3c4609a8effcd0fbac430f9b2/nb-NO/frontend.json", + "nl": "https://delivery.localazy.com/_a6406816515547716152c4539eac/_e0/7c203a8ac8bd48c3c4609a8effcd0fbac430f9b2/nl/frontend.json", + "pl": "https://delivery.localazy.com/_a6406816515547716152c4539eac/_e0/7c203a8ac8bd48c3c4609a8effcd0fbac430f9b2/pl/frontend.json", + "pt": "https://delivery.localazy.com/_a6406816515547716152c4539eac/_e0/7c203a8ac8bd48c3c4609a8effcd0fbac430f9b2/pt/frontend.json", + "pt_BR": "https://delivery.localazy.com/_a6406816515547716152c4539eac/_e0/7c203a8ac8bd48c3c4609a8effcd0fbac430f9b2/pt-BR/frontend.json", + "ru": "https://delivery.localazy.com/_a6406816515547716152c4539eac/_e0/7c203a8ac8bd48c3c4609a8effcd0fbac430f9b2/ru/frontend.json", + "sk": "https://delivery.localazy.com/_a6406816515547716152c4539eac/_e0/7c203a8ac8bd48c3c4609a8effcd0fbac430f9b2/sk/frontend.json", + "sv": "https://delivery.localazy.com/_a6406816515547716152c4539eac/_e0/7c203a8ac8bd48c3c4609a8effcd0fbac430f9b2/sv/frontend.json", + "tr": "https://delivery.localazy.com/_a6406816515547716152c4539eac/_e0/7c203a8ac8bd48c3c4609a8effcd0fbac430f9b2/tr/frontend.json", + "uk": "https://delivery.localazy.com/_a6406816515547716152c4539eac/_e0/7c203a8ac8bd48c3c4609a8effcd0fbac430f9b2/uk/frontend.json", + "uz": "https://delivery.localazy.com/_a6406816515547716152c4539eac/_e0/7c203a8ac8bd48c3c4609a8effcd0fbac430f9b2/uz/frontend.json", + "zh#Hans": "https://delivery.localazy.com/_a6406816515547716152c4539eac/_e0/7c203a8ac8bd48c3c4609a8effcd0fbac430f9b2/zh-Hans/frontend.json" } }, { @@ -234,25 +244,26 @@ const localazyMetadata: LocalazyMetadata = { file: "file.json", path: "", cdnFiles: { - "cs": "https://delivery.localazy.com/_a643710470041658449213cbf79a/_e0/5b69b0350dccfd47c245a5d41c1b9fdf6912cc6e/cs/file.json", - "da": "https://delivery.localazy.com/_a643710470041658449213cbf79a/_e0/5b69b0350dccfd47c245a5d41c1b9fdf6912cc6e/da/file.json", - "de": "https://delivery.localazy.com/_a643710470041658449213cbf79a/_e0/5b69b0350dccfd47c245a5d41c1b9fdf6912cc6e/de/file.json", - "en": "https://delivery.localazy.com/_a643710470041658449213cbf79a/_e0/5b69b0350dccfd47c245a5d41c1b9fdf6912cc6e/en/file.json", - "et": "https://delivery.localazy.com/_a643710470041658449213cbf79a/_e0/5b69b0350dccfd47c245a5d41c1b9fdf6912cc6e/et/file.json", - "fi": "https://delivery.localazy.com/_a643710470041658449213cbf79a/_e0/5b69b0350dccfd47c245a5d41c1b9fdf6912cc6e/fi/file.json", - "fr": "https://delivery.localazy.com/_a643710470041658449213cbf79a/_e0/5b69b0350dccfd47c245a5d41c1b9fdf6912cc6e/fr/file.json", - "hu": "https://delivery.localazy.com/_a643710470041658449213cbf79a/_e0/5b69b0350dccfd47c245a5d41c1b9fdf6912cc6e/hu/file.json", - "nb_NO": "https://delivery.localazy.com/_a643710470041658449213cbf79a/_e0/5b69b0350dccfd47c245a5d41c1b9fdf6912cc6e/nb-NO/file.json", - "nl": "https://delivery.localazy.com/_a643710470041658449213cbf79a/_e0/5b69b0350dccfd47c245a5d41c1b9fdf6912cc6e/nl/file.json", - "pl": "https://delivery.localazy.com/_a643710470041658449213cbf79a/_e0/5b69b0350dccfd47c245a5d41c1b9fdf6912cc6e/pl/file.json", - "pt": "https://delivery.localazy.com/_a643710470041658449213cbf79a/_e0/5b69b0350dccfd47c245a5d41c1b9fdf6912cc6e/pt/file.json", - "pt_BR": "https://delivery.localazy.com/_a643710470041658449213cbf79a/_e0/5b69b0350dccfd47c245a5d41c1b9fdf6912cc6e/pt-BR/file.json", - "ru": "https://delivery.localazy.com/_a643710470041658449213cbf79a/_e0/5b69b0350dccfd47c245a5d41c1b9fdf6912cc6e/ru/file.json", - "sk": "https://delivery.localazy.com/_a643710470041658449213cbf79a/_e0/5b69b0350dccfd47c245a5d41c1b9fdf6912cc6e/sk/file.json", - "sv": "https://delivery.localazy.com/_a643710470041658449213cbf79a/_e0/5b69b0350dccfd47c245a5d41c1b9fdf6912cc6e/sv/file.json", - "uk": "https://delivery.localazy.com/_a643710470041658449213cbf79a/_e0/5b69b0350dccfd47c245a5d41c1b9fdf6912cc6e/uk/file.json", - "uz": "https://delivery.localazy.com/_a643710470041658449213cbf79a/_e0/5b69b0350dccfd47c245a5d41c1b9fdf6912cc6e/uz/file.json", - "zh#Hans": "https://delivery.localazy.com/_a643710470041658449213cbf79a/_e0/5b69b0350dccfd47c245a5d41c1b9fdf6912cc6e/zh-Hans/file.json" + "cs": "https://delivery.localazy.com/_a6406816515547716152c4539eac/_e0/5b69b0350dccfd47c245a5d41c1b9fdf6912cc6e/cs/file.json", + "da": "https://delivery.localazy.com/_a6406816515547716152c4539eac/_e0/5b69b0350dccfd47c245a5d41c1b9fdf6912cc6e/da/file.json", + "de": "https://delivery.localazy.com/_a6406816515547716152c4539eac/_e0/5b69b0350dccfd47c245a5d41c1b9fdf6912cc6e/de/file.json", + "en": "https://delivery.localazy.com/_a6406816515547716152c4539eac/_e0/5b69b0350dccfd47c245a5d41c1b9fdf6912cc6e/en/file.json", + "et": "https://delivery.localazy.com/_a6406816515547716152c4539eac/_e0/5b69b0350dccfd47c245a5d41c1b9fdf6912cc6e/et/file.json", + "fi": "https://delivery.localazy.com/_a6406816515547716152c4539eac/_e0/5b69b0350dccfd47c245a5d41c1b9fdf6912cc6e/fi/file.json", + "fr": "https://delivery.localazy.com/_a6406816515547716152c4539eac/_e0/5b69b0350dccfd47c245a5d41c1b9fdf6912cc6e/fr/file.json", + "hu": "https://delivery.localazy.com/_a6406816515547716152c4539eac/_e0/5b69b0350dccfd47c245a5d41c1b9fdf6912cc6e/hu/file.json", + "nb_NO": "https://delivery.localazy.com/_a6406816515547716152c4539eac/_e0/5b69b0350dccfd47c245a5d41c1b9fdf6912cc6e/nb-NO/file.json", + "nl": "https://delivery.localazy.com/_a6406816515547716152c4539eac/_e0/5b69b0350dccfd47c245a5d41c1b9fdf6912cc6e/nl/file.json", + "pl": "https://delivery.localazy.com/_a6406816515547716152c4539eac/_e0/5b69b0350dccfd47c245a5d41c1b9fdf6912cc6e/pl/file.json", + "pt": "https://delivery.localazy.com/_a6406816515547716152c4539eac/_e0/5b69b0350dccfd47c245a5d41c1b9fdf6912cc6e/pt/file.json", + "pt_BR": "https://delivery.localazy.com/_a6406816515547716152c4539eac/_e0/5b69b0350dccfd47c245a5d41c1b9fdf6912cc6e/pt-BR/file.json", + "ru": "https://delivery.localazy.com/_a6406816515547716152c4539eac/_e0/5b69b0350dccfd47c245a5d41c1b9fdf6912cc6e/ru/file.json", + "sk": "https://delivery.localazy.com/_a6406816515547716152c4539eac/_e0/5b69b0350dccfd47c245a5d41c1b9fdf6912cc6e/sk/file.json", + "sv": "https://delivery.localazy.com/_a6406816515547716152c4539eac/_e0/5b69b0350dccfd47c245a5d41c1b9fdf6912cc6e/sv/file.json", + "tr": "https://delivery.localazy.com/_a6406816515547716152c4539eac/_e0/5b69b0350dccfd47c245a5d41c1b9fdf6912cc6e/tr/file.json", + "uk": "https://delivery.localazy.com/_a6406816515547716152c4539eac/_e0/5b69b0350dccfd47c245a5d41c1b9fdf6912cc6e/uk/file.json", + "uz": "https://delivery.localazy.com/_a6406816515547716152c4539eac/_e0/5b69b0350dccfd47c245a5d41c1b9fdf6912cc6e/uz/file.json", + "zh#Hans": "https://delivery.localazy.com/_a6406816515547716152c4539eac/_e0/5b69b0350dccfd47c245a5d41c1b9fdf6912cc6e/zh-Hans/file.json" } } ] diff --git a/frontend/locales/de.json b/frontend/locales/de.json index b1ee48009..b16b69edf 100644 --- a/frontend/locales/de.json +++ b/frontend/locales/de.json @@ -2,9 +2,9 @@ "action": { "back": "Zurück", "cancel": "Abbrechen", - "clear": "Löschen", + "clear": "Leeren", "close": "Schließen", - "collapse": "Zusammenbruch", + "collapse": "Reduzieren", "confirm": "Bestätigen", "continue": "Weiter", "edit": "Bearbeiten", @@ -12,7 +12,7 @@ "save": "Speichern", "save_and_continue": "Speichern und fortfahren", "sign_out": "Abmelden", - "start_over": "Von vorne anfangen" + "start_over": "Neu beginnen" }, "branding": { "privacy_policy": { @@ -26,12 +26,12 @@ }, "common": { "e2ee": "Ende-zu-Ende-Verschlüsselung", - "loading": "Lade …", + "loading": "Wird geladen …", "next": "Weiter", "password": "Passwort", "previous": "Zurück", "saved": "Gespeichert", - "saving": "Speichern..." + "saving": "Wird gespeichert …" }, "frontend": { "account": { @@ -39,21 +39,21 @@ "contact_info": "Kontaktinformation", "delete_account": { "alert_description": "Dieses Konto wird dauerhaft entfernt und du hast keinen Zugriff mehr auf deine Nachrichten.", - "alert_title": "Du bist kurz davor, alle deine Daten zu verlieren.", + "alert_title": "Du bist kurz davor, alle deine Daten zu verlieren", "button": "Account löschen", - "dialog_description": "Bestätige, dass du dein Konto löschen möchtest:\n\n\nDu kannst dein Konto nicht reaktivieren\nDu kannst dich nicht mehr anmelden\nNiemand kann deinen Benutzernamen (MXID) wieder verwenden, auch du nicht.\nDu verlässt alle Gruppen und Chats\nDu wirst vom Identitätsserver entfernt und niemand kann dich mit deiner E-Mail-Adresse oder Telefonnummer finden\n\nDeine alten Nachrichten sind für die jeweiligen Empfänger weiterhin sichtbar. Möchtest du deine gesendeten Nachrichten vor zukünftigen Gruppen-Besuchern verbergen?", + "dialog_description": "Bestätige, dass du dein Konto löschen möchtest:\n\n\nDu kannst dein Konto nicht reaktivieren\nDu kannst dich nicht mehr anmelden\nNiemand kann deinen Nutzernamen (MXID) wieder verwenden, auch du nicht.\nDu verlässt alle Gruppen und Chats\nDu wirst vom Identitätsserver entfernt und niemand kann dich mit deiner E-Mail-Adresse oder Telefonnummer finden\n\nDeine alten Nachrichten sind für die jeweiligen Empfänger weiterhin sichtbar. Möchtest du deine gesendeten Nachrichten vor zukünftigen Gruppen-Besuchern verbergen?", "dialog_title": "Dieses Konto löschen?", "erase_checkbox_label": "Ja, alle meine Nachrichten vor neuen Mitgliedern verbergen", - "incorrect_password": "Falsches Passwort, versuch's nochmal", + "incorrect_password": "Falsches Passwort, bitte versuche es erneut", "mxid_label": "Bestätige deine Matrix-ID ({{ mxid }})", - "mxid_mismatch": "Dieser Wert passt nicht zu deiner Matrix-ID.", + "mxid_mismatch": "Dieser Wert passt nicht zu deiner Matrix-ID", "password_label": "Gib dein Passwort ein, um weiterzumachen" }, "edit_profile": { "display_name_help": "Dies ist der öffentliche Nutzername.", "display_name_label": "Anzeigename", "title": "Profil bearbeiten", - "username_label": "Benutzername" + "username_label": "Nutzername" }, "password": { "change": "Passwort ändern", @@ -67,13 +67,13 @@ "title": "Dein Konto" }, "add_email_form": { - "email_denied_error": "Die eingegebene E-Mail wird von der Serverrichtlinie nicht zugelassen.", + "email_denied_error": "Die eingegebene E-Mail wird von der Serverrichtlinie nicht zugelassen", "email_field_help": "Gib eine alternative E-Mail-Adresse an, mit der du auf dieses Konto zugreifen kannst.", "email_field_label": "E-Mail-Adresse hinzufügen", "email_in_use_error": "Die eingegebene E-Mail wird bereits verwendet", "email_invalid_error": "Die eingegebene E-Mail-Adresse ist ungültig", - "incorrect_password_error": "Falsches Passwort, versuch's nochmal", - "password_confirmation": "Bestätige dein Passwort, um diese E-Mail-Adresse hinzuzufügen." + "incorrect_password_error": "Falsches Passwort, bitte versuche es erneut", + "password_confirmation": "Bestätige dein Passwort, um diese E-Mail-Adresse hinzuzufügen" }, "browser_session_details": { "current_badge": "Aktuell" @@ -99,22 +99,22 @@ "unknown": "Unbekannter Gerätetyp" }, "email_in_use": { - "heading": "Die E-Mail-Adresse {{email}} wird bereits verwendet." + "heading": "Die E-Mail-Adresse {{email}} wird bereits verwendet" }, "end_session_button": { "confirmation_modal_body_text": "Stelle sicher, dass du immer Zugriff auf ein anderes verifiziertes Gerät oder deinen Wiederherstellungsschlüssel hast, um zu vermeiden, dass dein verschlüsselter Chatverlauf verloren geht.", - "confirmation_modal_title": "Möchten Sie dieses Gerät wirklich entfernen?", + "confirmation_modal_title": "Möchtest du dieses Gerät wirklich entfernen?", "text": "Gerät entfernen" }, "error": { "hideDetails": "Details ausblenden", "showDetails": "Details anzeigen", - "subtitle": "Ein unerwarteter Fehler ist aufgetreten, bitte versuch's nochmal.", + "subtitle": "Ein unerwarteter Fehler ist aufgetreten. Bitte versuche es erneut.", "title": "Etwas ist schief gelaufen" }, "errors": { "field_required": "Dieses Feld ist ein Pflichtfeld", - "rate_limit_exceeded": "Du hast in kurzer Zeit zu viele Anfragen gestellt. Warte bitte ein paar Minuten und versuch's nochmal." + "rate_limit_exceeded": "Du hast in kurzer Zeit zu viele Anfragen gestellt. Bitte warte ein paar Minuten und versuche es erneut." }, "last_active": { "active_date": "Aktiv {{relativeDate}}", @@ -145,15 +145,15 @@ "current_password_label": "Aktuelles Passwort", "failure": { "description": { - "account_locked": "Dein Konto ist gesperrt und kann im Moment nicht wiederhergestellt werden. Wenn du das nicht erwartet hast, wende dich bitte an deinen Server-Admin.", + "account_locked": "Dein Konto ist gesperrt und kann derzeit nicht wiederhergestellt werden. Wenn du das nicht erwartet hast, wende dich an deinen Kontoanbieter.", "expired_recovery_ticket": "Der Link zur Kontowiederherstellung ist abgelaufen. Bitte fang den Prozess noch mal von vorne an.", "invalid_new_password": "Das neue Passwort, das du gewählt hast, ist ungültig; es entspricht möglicherweise nicht den Sicherheitsrichtlinien.", "no_current_password": "Du hast kein aktuelles Passwort.", "no_such_recovery_ticket": "Der Link zum Wiederherstellen ist nicht gültig. Wenn du den Link aus der E-Mail zum Wiederherstellen kopiert hast, schau bitte nach, ob du den vollständigen Link kopiert hast.", "password_changes_disabled": "Passwortänderungen sind deaktiviert.", "recovery_ticket_already_used": "Der Wiederherstellungslink wurde bereits verwendet. Er kann nicht erneut verwendet werden.", - "unspecified": "Das könnte ein vorübergehendes Problem sein, also versuch's später nochmal. Wenn das Problem weiterhin besteht, wende dich bitte an deinen Server-Admin.", - "wrong_password": "Das Passwort, das du als dein aktuelles Passwort angegeben hast, ist falsch. Versuch's bitte nochmal." + "unspecified": "Das könnte ein vorübergehendes Problem sein. Bitte versuche es später erneut. Wenn das Problem weiterhin besteht, wende dich an deinen Kontoanbieter.", + "wrong_password": "Das Passwort, das du als aktuelles Passwort angegeben hast, ist falsch. Bitte versuche es erneut." }, "title": "Aktualisierung des Passworts fehlgeschlagen" }, @@ -170,7 +170,7 @@ }, "password_reset": { "consumed": { - "subtitle": "Um ein neues Passwort zu erstellen, fang einfach von vorne an und wähle „Passwort vergessen“.", + "subtitle": "Um ein neues Passwort zu erstellen, beginne neu und wähle „Passwort vergessen“.", "title": "Der Link zum Zurücksetzen deines Passworts wurde bereits verwendet" }, "expired": { @@ -214,11 +214,11 @@ "extended_repeat": "Wiederholte Zeichenmuster wie „abcabcabc“ sind leicht zu erraten.", "key_pattern": "Kurze Eingaben sind leicht zu erraten.", "names_by_themselves": "Einzelne Vor- oder Nachnamen sind leicht zu erraten.", - "pwned": "Dein Passwort wurde durch eine Datenpanne im Internet preisgegeben.", + "pwned": "Dieses Passwort wird zu häufig verwendet. Bitte wähle ein Passwort, das schwerer zu erraten ist.", "recent_years": "Die letzten Jahre sind leicht zu erraten.", "sequences": "Gängige Zeichenfolgen wie „abc“ sind leicht zu erraten.", "similar_to_common": "Dies ähnelt einem häufig verwendeten Passwort.", - "simple_repeat": "Sich wiederholende Zeichen wie \"aaa\" sind leicht zu erraten.", + "simple_repeat": "Sich wiederholende Zeichen wie „aaa“ sind leicht zu erraten.", "straight_row": "Gerade Reihen von Tasten auf deiner Tastatur sind leicht zu erraten.", "top_hundred": "Dies ist ein häufig verwendetes Passwort.", "top_ten": "Dies ist ein häufig verwendetes Passwort.", @@ -229,27 +229,27 @@ "reset_cross_signing": { "cancelled": { "description_1": "Du kannst dieses Fenster schließen und zur App zurückgehen, um weiterzumachen.", - "description_2": "Wenn du dich überall abgemeldet hast und deinen Wiederherstellungs-Schlüssel nicht mehr weißt, musst du deine Identität zurücksetzen.", - "heading": "Identitätszurücksetzung abgebrochen." + "description_2": "Wenn du keinen Zugriff auf ein anderes verifiziertes Gerät hast und deinen Wiederherstellungsschlüssel nicht hast, musst du deine digitale Identität zurücksetzen, um die App weiter nutzen zu können.", + "heading": "Zurücksetzen der digitalen Identität abgebrochen" }, - "description": "Wenn du auf keinem anderen Gerät angemeldet bist und deinen Wiederherstellungs-Schlüssel verloren hast, musst du deine Identität zurücksetzen, um die App weiter nutzen zu können.", + "description": "Wenn du keinen Zugriff auf ein anderes verifiziertes Gerät hast und deinen Wiederherstellungsschlüssel nicht hast, musst du deine digitale Identität zurücksetzen, um die App weiter nutzen zu können.", "effect_list": { - "neutral_1": "Du verlierst alle Nachrichten, die nur auf dem Server gespeichert sind.", + "neutral_1": "Du verlierst alle Nachrichten, die nur auf dem Server gespeichert sind", "neutral_2": "Du musst alle deine Geräte und Kontakte nochmal verifizieren.", - "positive_1": "Deine Kontodaten, Kontakte, Einstellungen und Chat-Liste bleiben erhalten." + "positive_1": "Deine Kontodaten, Kontakte, Einstellungen und Chat-Liste bleiben erhalten" }, "failure": { - "description": "Das könnte ein vorübergehendes Problem sein, also versuch's später nochmal. Wenn das Problem weiterhin besteht, wende dich bitte an deinen Server-Admin.", - "heading": "Zurücksetzen der Krypto-Identität konnte nicht zugelassen werden" + "description": "Das könnte ein vorübergehendes Problem sein. Bitte versuche es später erneut. Wenn das Problem weiterhin besteht, wende dich an deinen Kontoanbieter.", + "heading": "Zurücksetzen der digitalen Identität konnte nicht zugelassen werden" }, "finish_reset": "Reset beenden", - "heading": "Erstelle eine neue Identität, solltest du sie nicht auf andere Weise bestätigen können.", + "heading": "Setze deine digitale Identität zurück, falls du sie nicht anders bestätigen kannst", "start_reset": "Reset starten", "success": { - "description": "Das Zurücksetzen der Identität wurde für die nächsten {{minutes}} Minuten genehmigt. Du kannst dieses Fenster schließen und zur App zurückkehren, um fortzufahren.", - "heading": "Identität erfolgreich zurückgesetzt. Geh zurück zur App, um den Vorgang abzuschließen." + "description": "Das Zurücksetzen der digitalen Identität wurde für die nächsten {{minutes}} Minuten genehmigt. Du kannst dieses Fenster schließen und zur App zurückkehren, um fortzufahren.", + "heading": "Digitale Identität erfolgreich zurückgesetzt. Kehre zur App zurück, um den Vorgang abzuschließen." }, - "warning": "Setze deine Identität nur zurück, wenn du keinen Zugriff auf ein anderes angemeldetes Gerät hast und deinen Wiederherstellungsschlüssel verloren hast." + "warning": "Setze deine digitale Identität nur zurück, wenn du keinen Zugriff auf ein anderes verifiziertes Gerät hast und deinen Wiederherstellungsschlüssel nicht hast." }, "session": { "client_id_label": "Client-ID", @@ -283,8 +283,8 @@ "delete_button_confirmation_modal": { "action": "E-Mail löschen", "body": "Diese E-Mail löschen?", - "incorrect_password": "Falsches Passwort, versuch's nochmal", - "password_confirmation": "Bestätige dein Passwort, um diese E-Mail-Adresse zu löschen." + "incorrect_password": "Falsches Passwort, bitte versuche es erneut", + "password_confirmation": "Bestätige dein Passwort, um diese E-Mail-Adresse zu löschen" }, "delete_button_title": "E-Mail-Adresse entfernen", "email": "E-Mail" @@ -302,8 +302,8 @@ "inactive_90_days": "Alle deine Sitzungen waren in den letzten 90 Tagen aktiv." }, "num_sessions_filtered_header": { - "header:one": "{{count}} Geräte entsprechen Ihren Suchkriterien ({{unfiltered_session_total}})", - "header:other": "{{count}} Geräte entsprechen Ihren Suchkriterien ({{unfiltered_session_total}})", + "header:one": "{{count}} Gerät entspricht deinen Suchkriterien ({{unfiltered_session_total}})", + "header:other": "{{count}} Geräte entsprechen deinen Suchkriterien ({{unfiltered_session_total}})", "unfiltered_session_total:one": "{{count}} insgesamt", "unfiltered_session_total:other": "{{count}} insgesamt" }, @@ -328,7 +328,7 @@ "heading": "Bestätige deine E-Mail", "invalid_code_alert": { "description": "Überprüfe den Code, der an deine E-Mail-Adresse gesendet wurde, und aktualisiere die folgenden Felder, um fortzufahren.", - "title": "Du hast den falschen Code eingegeben." + "title": "Du hast den falschen Code eingegeben" }, "resend_code": "Code erneut senden" } @@ -337,7 +337,7 @@ "scope": { "edit_profile": "Bearbeite dein Profil und deine Kontaktdaten", "manage_sessions": "Verwalte deine Geräte und Sitzungen", - "mas_admin": "Beliebige Benutzer verwalten (urn:mas:admin)", + "mas_admin": "Beliebige Nutzer verwalten (urn:mas:admin)", "send_messages": "Neue Nachrichten in deinem Namen senden", "synapse_admin": "Den Synapse-Homeserver verwalten (urn:synapse:admin:*)", "view_messages": "Zeig deine vorhandenen Nachrichten und Daten an", diff --git a/frontend/locales/et.json b/frontend/locales/et.json index 0d986c0dc..5514e0e75 100644 --- a/frontend/locales/et.json +++ b/frontend/locales/et.json @@ -145,14 +145,14 @@ "current_password_label": "Senine salasõna", "failure": { "description": { - "account_locked": "Sinu kasutajakonto on lukustatud ning salasõna taastamist ei ole hetkel võimalik teha. Kui antud olukord tundub sulle kahtlane, siis palun teavita sellest sinu serveri haldajat.", + "account_locked": "Sinu kasutajakonto on lukustatud ning salasõna taastamist ei ole hetkel võimalik teha. Kui antud olukord tundub sulle kahtlane, siis palun teavita sellest sinu teenusepakkujat.", "expired_recovery_ticket": "Salasõna taastamiseks mõeldud link on aegunud. Palun alusta taastamisprotsessi algusest.", "invalid_new_password": "Sinu sisestatud uus salasõna pole korrektne, ta ilmselt ei vasta seadistatud turvareeglitele.", "no_current_password": "Sul on hetkel kehtiv salasõna määramata.", "no_such_recovery_ticket": "Salasõna taastamiseks mõeldud link on vigane. Kui sa kopeerisid ja e-kirjast, siis palun kontrolli, et kopeerisid kogu lingi ühe tervikuna.", "password_changes_disabled": "Salasõnade muutmine pole lubatud.", "recovery_ticket_already_used": "Sa oled salasõna taastamiseks mõeldud linki juba kasutanud ja seda ei saa uuesti teha.", - "unspecified": "See ilmselt on ajutine probleem, palun proovi mõne aja pärast uuesti. Kui viga ei kao, siis abi saad serveri haldajalt.", + "unspecified": "See ilmselt on ajutine probleem, palun proovi mõne aja pärast uuesti. Kui viga ei kao, siis abi saad oma teenusepakkujalt.", "wrong_password": "Sinu sisestatud salasõna ei vasta hetkel kehtivale salasõnale. Palun proovi uuesti.," }, "title": "Salasõna uuendamine ei õnnestunud" @@ -239,7 +239,7 @@ "positive_1": "Sinu kasutajakonto andmed, kontaktid, eelistused ja vestluste loendid jäävad muutmata" }, "failure": { - "description": "See ilmselt on ajutine probleem, palun proovi mõne aja pärast uuesti. Kui viga ei kao, siis abi saad serveri haldajalt.", + "description": "See ilmselt on ajutine probleem, palun proovi mõne aja pärast uuesti. Kui viga ei kao, siis abi saad oma teenusepakkujalt.", "heading": "Krüptoidentiteedi lähtestamise lubamine ei õnnestunud" }, "finish_reset": "Lõpeta lähtestamine", diff --git a/frontend/locales/fr.json b/frontend/locales/fr.json index 5fc3372e3..c6c8bc0cb 100644 --- a/frontend/locales/fr.json +++ b/frontend/locales/fr.json @@ -103,7 +103,7 @@ }, "end_session_button": { "confirmation_modal_body_text": "Assurez-vous de toujours avoir accès à un autre appareil vérifié ou à votre clé de récupération afin d'éviter de perdre l'historique de vos discussions chiffrées.", - "confirmation_modal_title": "Êtes-vous sûr de vouloir terminer cette session ?", + "confirmation_modal_title": "Êtes-vous sûr de vouloir supprimer cet appareil ?", "text": "Supprimer l’appareil" }, "error": { diff --git a/frontend/locales/nl.json b/frontend/locales/nl.json index 774601087..0c30ef5fb 100644 --- a/frontend/locales/nl.json +++ b/frontend/locales/nl.json @@ -99,7 +99,7 @@ "unknown": "Onbekend apparaattype" }, "email_in_use": { - "heading": "The email address {{email}} is already in use." + "heading": "The email address {{email}} is already in use" }, "end_session_button": { "confirmation_modal_body_text": "Make sure you always have access to another verified device or your recovery key to avoid losing your encrypted chat history.", @@ -145,14 +145,14 @@ "current_password_label": "Huidig wachtwoord", "failure": { "description": { - "account_locked": "Your account is locked and can not be recovered at this time. If this is not expected, please contact your server administrator.", + "account_locked": "Your account is locked and can not be recovered at this time. If this is not expected, please contact your account provider.", "expired_recovery_ticket": "The recovery link has expired. Please start the account recovery process again from the start.", "invalid_new_password": "The new password you chose is invalid; it may not meet the configured security policy.", "no_current_password": "You don't have a current password.", "no_such_recovery_ticket": "The recovery link is invalid. If you copied the link from the recovery e-mail, please check the full link was copied.", "password_changes_disabled": "Password changes are disabled.", "recovery_ticket_already_used": "The recovery link has already been used. It cannot be used again.", - "unspecified": "This might be a temporary problem, so please try again later. If the problem persists, please contact your server administrator.", + "unspecified": "This might be a temporary problem, so please try again later. If the problem persists, please contact your account provider.", "wrong_password": "The password you supplied as your current password is incorrect. Please try again." }, "title": "Failed to update password" @@ -230,7 +230,7 @@ "cancelled": { "description_1": "You can close this window and go back to the app to continue.", "description_2": "If don’t have access to any other verified devices and you don’t have your recovery key, then you’ll need to reset your digital identity to continue using the app.", - "heading": "Digital identity reset cancelled." + "heading": "Digital identity reset cancelled" }, "description": "Als u niet bent aangemeld bij andere apparaten en u bent uw herstelsleutel kwijt, moet u uw identiteit opnieuw instellen om de app te kunnen blijven gebruiken.", "effect_list": { @@ -239,7 +239,7 @@ "positive_1": "Your account details, contacts, preferences, and chat list will be kept" }, "failure": { - "description": "This might be a temporary problem, so please try again later. If the problem persists, please contact your server administrator.", + "description": "This might be a temporary problem, so please try again later. If the problem persists, please contact your account provider.", "heading": "Failed to allow digital identity reset" }, "finish_reset": "Finish reset", diff --git a/frontend/locales/pl.json b/frontend/locales/pl.json index 1134698e4..1caf5c6a7 100644 --- a/frontend/locales/pl.json +++ b/frontend/locales/pl.json @@ -25,7 +25,7 @@ } }, "common": { - "e2ee": "Szyfrowanie typu end-to-end", + "e2ee": "Szyfrowanie end-to-end", "loading": "Wczytywanie…", "next": "Dalej", "password": "Hasło", @@ -35,7 +35,7 @@ }, "frontend": { "account": { - "account_password": "Hasło do konta", + "account_password": "Hasło konta", "contact_info": "Dane kontaktowe", "delete_account": { "alert_description": "To konto zostanie trwale usunięte i nie będziesz już mieć dostępu do żadnych wiadomości.", @@ -76,7 +76,7 @@ "password_confirmation": "Potwierdź hasło do swojego konta, aby dodać ten adres e-mail" }, "browser_session_details": { - "current_badge": "Aktualny" + "current_badge": "Aktualne" }, "browser_sessions_overview": { "body:one": "{{count}} aktywna sesja", @@ -120,7 +120,7 @@ "last_active": { "active_date": "Aktywne {{relativeDate}}", "active_now": "Aktywne teraz", - "inactive_90_days": "Nieaktywny przez ponad 90 dni" + "inactive_90_days": "Nieaktywne przez ponad 90 dni" }, "nav": { "device_limit_error": "Osiągnięto limit urządzeń", @@ -146,14 +146,14 @@ "current_password_label": "Aktualne hasło", "failure": { "description": { - "account_locked": "Twoje konto jest zablokowane i nie można go obecnie odzyskać. Jeśli nie jest to oczekiwane, skontaktuj się z administratorem serwera.", + "account_locked": "Twoje konto jest zablokowane i nie można go obecnie odzyskać. Jeśli nie spodziewałeś się tej informacji, skontaktuj się z dostawcą konta.", "expired_recovery_ticket": "Link do odzyskiwania wygasł. Rozpocznij proces odzyskiwania konta od początku.", "invalid_new_password": "Wybrane nowe hasło jest nieprawidłowe i może nie spełniać skonfigurowanych zasad bezpieczeństwa.", "no_current_password": "Nie masz aktualnego hasła.", "no_such_recovery_ticket": "Link odzyskiwania jest nieprawidłowy. Jeśli skopiowałeś link z e-maila odzyskiwania, sprawdź, czy został skopiowany w całości.", "password_changes_disabled": "Możliwość zmiany hasła jest wyłączona.", "recovery_ticket_already_used": "Link odzyskiwania został już użyty. Nie można go użyć ponownie.", - "unspecified": "To może być problem tymczasowy, więc spróbuj ponownie później. Jeśli problem będzie się powtarzał, skontaktuj się z administratorem serwera.", + "unspecified": "To może być problem tymczasowy, spróbuj ponownie później. Jeśli problem będzie się powtarzał, skontaktuj się z dostawcą konta.", "wrong_password": "Podane hasło jest nieprawidłowe. Spróbuj ponownie." }, "title": "Nie udało się zaktualizować hasła" @@ -233,14 +233,14 @@ "description_2": "Jeśli wylogowałeś się z dowolnego miejsca i nie pamiętasz kodu odzyskiwania, nadal musisz zresetować swoją tożsamość.", "heading": "Resetowanie tożsamości zostało anulowane." }, - "description": "Jeśli nie zalogowałeś się na żadnym innym urządzeniu i utraciłeś klucz odzyskiwania, musisz zresetować swoją tożsamość, aby móc nadal korzystać z aplikacji.", + "description": "Jeśli nie masz dostępu do innych zweryfikowanych urządzeń i nie posiadasz klucza odzyskiwania, musisz zresetować swoją tożsamość cyfrową, aby móc nadal korzystać z aplikacji.", "effect_list": { "neutral_1": "Utracisz całą historię wiadomości przechowywaną wyłącznie na serwerze", "neutral_2": "Będziesz musiał ponownie zweryfikować wszystkie swoje istniejące urządzenia i kontakty", "positive_1": "Twoje dane konta, kontakty, preferencje i lista czatów zostaną zachowane" }, "failure": { - "description": "To może być problem tymczasowy, więc spróbuj ponownie później. Jeśli problem będzie się powtarzał, skontaktuj się z administratorem serwera.", + "description": "To może być problem tymczasowy, spróbuj ponownie później. Jeśli problem będzie się powtarzał, skontaktuj się z dostawcą konta.", "heading": "Nie udało się zezwolić na zresetowanie tożsamości kryptograficznej" }, "finish_reset": "Zakończ resetowanie", @@ -254,14 +254,14 @@ }, "session": { "client_id_label": "Identyfikator klienta", - "current": "Aktualny", + "current": "Aktualne", "device_id_label": "Identyfikator urządzenia", "finished_label": "Zakończone", "generic_browser_session": "Sesja przeglądarki", "ip_label": "Adres IP", - "last_active_label": "Ostatnio aktywny", + "last_active_label": "Ostatnio aktywne", "name_for_platform": "{{name}} dla {{platform}}", - "scopes_label": "Zakresy", + "scopes_label": "Możliwości", "set_device_name": { "help": "Ustaw nazwę, która ułatwi identyfikację tego urządzenia.", "label": "Nazwa urządzenia", @@ -345,10 +345,10 @@ "edit_profile": "Edytuj swój profil i dane kontaktowe", "manage_sessions": "Zarządzaj swoimi urządzeniami i sesjami", "mas_admin": "Zarządzaj użytkownikami (urn:mas:admin)", - "send_messages": "Wysyłaj nowe wiadomości w Twoim imieniu", + "send_messages": "Wysyłanie nowych wiadomości w Twoim imieniu", "synapse_admin": "Administrowanie serwerem (urn:synapse:admin:*)", - "view_messages": "Przegląd istniejących wiadomości i danych", - "view_profile": "Przegląd informacji o profilu i danych kontaktowych" + "view_messages": "Przeglądanie istniejących wiadomości i danych", + "view_profile": "Przeglądanie informacji profilowych i danych kontaktowych" } } } \ No newline at end of file diff --git a/frontend/locales/pt-BR.json b/frontend/locales/pt-BR.json index 73ce0149c..24cbc1cde 100644 --- a/frontend/locales/pt-BR.json +++ b/frontend/locales/pt-BR.json @@ -99,7 +99,7 @@ "unknown": "Unknown device type" }, "email_in_use": { - "heading": "The email address {{email}} is already in use." + "heading": "The email address {{email}} is already in use" }, "end_session_button": { "confirmation_modal_body_text": "Make sure you always have access to another verified device or your recovery key to avoid losing your encrypted chat history.", @@ -145,14 +145,14 @@ "current_password_label": "Senha atual", "failure": { "description": { - "account_locked": "Your account is locked and can not be recovered at this time. If this is not expected, please contact your server administrator.", + "account_locked": "Your account is locked and can not be recovered at this time. If this is not expected, please contact your account provider.", "expired_recovery_ticket": "The recovery link has expired. Please start the account recovery process again from the start.", "invalid_new_password": "The new password you chose is invalid; it may not meet the configured security policy.", "no_current_password": "You don't have a current password.", "no_such_recovery_ticket": "The recovery link is invalid. If you copied the link from the recovery e-mail, please check the full link was copied.", "password_changes_disabled": "Password changes are disabled.", "recovery_ticket_already_used": "The recovery link has already been used. It cannot be used again.", - "unspecified": "This might be a temporary problem, so please try again later. If the problem persists, please contact your server administrator.", + "unspecified": "This might be a temporary problem, so please try again later. If the problem persists, please contact your account provider.", "wrong_password": "The password you supplied as your current password is incorrect. Please try again." }, "title": "Failed to update password" @@ -230,7 +230,7 @@ "cancelled": { "description_1": "You can close this window and go back to the app to continue.", "description_2": "If don’t have access to any other verified devices and you don’t have your recovery key, then you’ll need to reset your digital identity to continue using the app.", - "heading": "Digital identity reset cancelled." + "heading": "Digital identity reset cancelled" }, "description": "If don’t have access to any other verified devices and you don’t have your recovery key, then you’ll need to reset your digital identity to continue using the app.", "effect_list": { @@ -239,7 +239,7 @@ "positive_1": "Your account details, contacts, preferences, and chat list will be kept" }, "failure": { - "description": "This might be a temporary problem, so please try again later. If the problem persists, please contact your server administrator.", + "description": "This might be a temporary problem, so please try again later. If the problem persists, please contact your account provider.", "heading": "Failed to allow digital identity reset" }, "finish_reset": "Finish reset", diff --git a/frontend/locales/sk.json b/frontend/locales/sk.json index c4c40df3e..2a6cdfe68 100644 --- a/frontend/locales/sk.json +++ b/frontend/locales/sk.json @@ -99,7 +99,7 @@ "unknown": "Unknown device type" }, "email_in_use": { - "heading": "The email address {{email}} is already in use." + "heading": "The email address {{email}} is already in use" }, "end_session_button": { "confirmation_modal_body_text": "Make sure you always have access to another verified device or your recovery key to avoid losing your encrypted chat history.", @@ -145,14 +145,14 @@ "current_password_label": "Súčasné heslo", "failure": { "description": { - "account_locked": "Your account is locked and can not be recovered at this time. If this is not expected, please contact your server administrator.", + "account_locked": "Your account is locked and can not be recovered at this time. If this is not expected, please contact your account provider.", "expired_recovery_ticket": "The recovery link has expired. Please start the account recovery process again from the start.", "invalid_new_password": "The new password you chose is invalid; it may not meet the configured security policy.", "no_current_password": "You don't have a current password.", "no_such_recovery_ticket": "The recovery link is invalid. If you copied the link from the recovery e-mail, please check the full link was copied.", "password_changes_disabled": "Password changes are disabled.", "recovery_ticket_already_used": "The recovery link has already been used. It cannot be used again.", - "unspecified": "This might be a temporary problem, so please try again later. If the problem persists, please contact your server administrator.", + "unspecified": "This might be a temporary problem, so please try again later. If the problem persists, please contact your account provider.", "wrong_password": "The password you supplied as your current password is incorrect. Please try again." }, "title": "Failed to update password" @@ -230,7 +230,7 @@ "cancelled": { "description_1": "You can close this window and go back to the app to continue.", "description_2": "If don’t have access to any other verified devices and you don’t have your recovery key, then you’ll need to reset your digital identity to continue using the app.", - "heading": "Digital identity reset cancelled." + "heading": "Digital identity reset cancelled" }, "description": "If don’t have access to any other verified devices and you don’t have your recovery key, then you’ll need to reset your digital identity to continue using the app.", "effect_list": { @@ -239,7 +239,7 @@ "positive_1": "Your account details, contacts, preferences, and chat list will be kept" }, "failure": { - "description": "This might be a temporary problem, so please try again later. If the problem persists, please contact your server administrator.", + "description": "This might be a temporary problem, so please try again later. If the problem persists, please contact your account provider.", "heading": "Failed to allow digital identity reset" }, "finish_reset": "Finish reset", diff --git a/frontend/locales/tr.json b/frontend/locales/tr.json new file mode 100644 index 000000000..8f552a68b --- /dev/null +++ b/frontend/locales/tr.json @@ -0,0 +1,347 @@ +{ + "action": { + "back": "Geri", + "cancel": "İptal", + "clear": "Temizle", + "close": "Kapat", + "collapse": "Daralt", + "confirm": "Onayla", + "continue": "Devam Et", + "edit": "Düzenle", + "expand": "Genişlet", + "save": "Kaydet", + "save_and_continue": "Kaydet ve devam et", + "sign_out": "Çıkış Yap", + "start_over": "Baştan başla" + }, + "branding": { + "privacy_policy": { + "alt": "Hizmet gizlilik politikasına bağlantı", + "link": "Gizlilik Politikası" + }, + "terms_and_conditions": { + "alt": "Hizmet şartları ve koşullarına bağlantı", + "link": "Şartlar & Koşullar" + } + }, + "common": { + "e2ee": "Uçtan uca şifreleme", + "loading": "Yükleniyor…", + "next": "Sonraki", + "password": "Şifre", + "previous": "Önceki", + "saved": "Kaydedildi", + "saving": "Kaydediliyor…" + }, + "frontend": { + "account": { + "account_password": "Hesap parolası", + "contact_info": "İletişim bilgileri", + "delete_account": { + "alert_description": "Bu hesap kalıcı olarak silinecek ve artık mesajlarınıza erişemeyeceksiniz.", + "alert_title": "Verilerinizin tamamını kaybetmek üzeresiniz.", + "button": "Hesabı sil", + "dialog_description": " Hesabınızı silmek istediğinizi onaylayın: \n\n \n Hesabınızı yeniden etkinleştiremeyeceksiniz \n Artık oturum açamayacaksınız \n Siz de dahil olmak üzere hiç kimse kullanıcı adınızı (MXID) yeniden kullanamayacak \n Katıldığınız tüm odalardan ve doğrudan mesajlardan ayrılacaksınız \n Kimlik sunucusundan kaldırılacaksınız ve hiç kimse sizi e-posta adresiniz veya telefon numaranızla bulamayacak \n \n Eski mesajlarınız, onları alan kişiler tarafından hala görülebilir. Gelecekte odalara katılan kişilerden gönderdiğiniz mesajları gizlemek ister misiniz? ", + "dialog_title": "Bu hesabı silmek istiyor musunuz?", + "erase_checkbox_label": "Evet, yeni katılanlardan tüm mesajlarımı gizle.", + "incorrect_password": "Hatalı parola, lütfen tekrar deneyin", + "mxid_label": "Matrix Kimliğinizi ({{ mxid }}) onaylayın", + "mxid_mismatch": "Bu değer, Matrix Kimliğinizle eşleşmiyor.", + "password_label": "Devam etmek için şifrenizi girin" + }, + "edit_profile": { + "display_name_help": "Oturum açtığınız her yerde diğerler kullanıcılar bunu görecektir.", + "display_name_label": "Görünen ad", + "title": "Profili düzenle", + "username_label": "Kullanıcı Adı" + }, + "password": { + "change": "Parola değiştir", + "change_disabled": "Yönetici tarafından parola değiştirme özelliği devre dışı bırakılmıştır.", + "label": "Şifre" + }, + "sign_out": { + "button": "Hesaptan çıkış yap", + "dialog": "Bu hesaptan çıkış yapmak ister misiniz?" + }, + "title": "Hesabınız" + }, + "add_email_form": { + "email_denied_error": "Girilen e-posta adresi, sunucu politikası gereği kabul edilmiyor", + "email_field_help": "Bu hesaba erişmek için kullanabileceğiniz alternatif bir e-posta adresi ekleyin.", + "email_field_label": "E-posta ekle", + "email_in_use_error": "Girilen e-posta adresi zaten kullanımda", + "email_invalid_error": "The entered email is invalid", + "incorrect_password_error": "Hatalı parola, lütfen tekrar deneyin", + "password_confirmation": "Bu e-posta adresini eklemek için hesap şifrenizi onaylayın" + }, + "browser_session_details": { + "current_badge": "Geçerli" + }, + "browser_sessions_overview": { + "body:one": "{{count}} aktif oturum", + "body:other": "{{count}} aktif oturumlar", + "heading": "Tarayıcılar", + "no_active_sessions": { + "default": "Herhangi bir web tarayıcısına giriş yapmadınız.", + "inactive_90_days": "Son 90 gün içinde tüm oturumlarınız aktif olmuştur." + }, + "view_all_button": "Tümünü görüntüle" + }, + "compat_session_detail": { + "client_details_title": "Müşteri bilgileri", + "name": "İsim" + }, + "device_type_icon_label": { + "mobile": "Mobil", + "pc": "Bilgisayar", + "tablet": "Tablet", + "unknown": "Bilinmeyen cihaz türü" + }, + "email_in_use": { + "heading": "{{email}} e-posta adresi zaten kullanımda." + }, + "end_session_button": { + "confirmation_modal_body_text": "Şifrelenmiş sohbet geçmişinizi kaybetmemek için her zaman başka bir doğrulanmış cihaza veya kurtarma anahtarınıza erişiminizin olduğundan emin olun.", + "confirmation_modal_title": "Bu cihazı kaldırmak istediğinizden emin misiniz?", + "text": "Cihazı kaldır" + }, + "error": { + "hideDetails": "Ayrıntıları gizle", + "showDetails": "Detayları göster", + "subtitle": "Beklenmeyen bir hata oluştu. Lütfen tekrar deneyin.", + "title": "Bir şeyler ters gitti." + }, + "errors": { + "field_required": "Bu alan zorunludur", + "rate_limit_exceeded": "Kısa süre içinde çok fazla istekte bulundunuz. Lütfen birkaç dakika bekleyin ve tekrar deneyin." + }, + "last_active": { + "active_date": "Aktif {{relativeDate}}", + "active_now": "Şu anda aktif", + "inactive_90_days": "90 günden fazla süredir aktif değil" + }, + "nav": { + "device_limit_error": "Cihaz limitine ulaşıldı", + "devices": "Cihazlar", + "plan": "Plan", + "settings": "Ayarlar" + }, + "not_found_alert_title": "Bulunamadı.", + "oauth2_client_detail": { + "details_title": "Müşteri bilgileri", + "name": "İsim", + "policy": "Politika", + "terms": "Hizmet şartları" + }, + "oauth2_session_detail": { + "client_details_name": "İsim", + "client_title": "Müşteri bilgileri" + }, + "pagination_controls": { + "total": "Toplam: {{totalCount}}" + }, + "password_change": { + "current_password_label": "Mevcut parola", + "failure": { + "description": { + "account_locked": "Hesabınız kilitlendi ve şu anda kurtarılamıyor. Bu durum beklenmedik bir şeyse, lütfen sunucu yöneticinizle iletişime geçin.", + "expired_recovery_ticket": "Kurtarma bağlantısının süresi doldu. Lütfen hesap kurtarma işlemine baştan başlayın.", + "invalid_new_password": "Seçtiğiniz yeni parola geçersiz; yapılandırılmış güvenlik politikasına uymuyor olabilir.", + "no_current_password": "Şu anda geçerli bir parolanız yok.", + "no_such_recovery_ticket": "Kurtarma bağlantısı geçersiz. Kurtarma e-postasındaki bağlantıyı kopyaladıysanız, lütfen bağlantının tamamının kopyalandığından emin olun.", + "password_changes_disabled": "Parola değiştirme özelliği devre dışı bırakılmıştır.", + "recovery_ticket_already_used": "Kurtarma bağlantısı daha önce kullanıldı. Tekrar kullanılamaz.", + "unspecified": "Bu geçici bir sorun olabilir, lütfen daha sonra tekrar deneyin. Sorun devam ederse, lütfen sunucu yöneticinizle iletişime geçin.", + "wrong_password": "Girdiğiniz geçerli parola yanlış. Lütfen tekrar deneyin." + }, + "title": "Parola güncelleme başarısız oldu." + }, + "new_password_again_label": "Yeni parolayı tekrar girin.", + "new_password_label": "Yeni parola", + "passwords_match": "Parolalar eşleşiyor!", + "passwords_no_match": "Parolalar eşleşmiyor", + "subtitle": "Hesabınız için yeni bir parola belirleyin.", + "success": { + "description": "Parolanız başarıyla güncellendi.", + "title": "Parola güncellendi" + }, + "title": "Parolanızı değiştirin" + }, + "password_reset": { + "consumed": { + "subtitle": "Yeni bir parola oluşturmak için, baştan başlayın ve \"Parolamı unuttum\" seçeneğini seçin.", + "title": "Parolanızı sıfırlamak için kullanılan bağlantı daha önce kullanıldı" + }, + "expired": { + "resend_email": "Resend email", + "subtitle": "{{email}} adresine gönderilecek yeni bir e-posta isteği gönderin.", + "title": "Parolanızı sıfırlama bağlantısının süresi doldu" + }, + "subtitle": "Hesabınız için yeni bir parola belirleyin.", + "title": "Parolanızı sıfırlayın" + }, + "password_strength": { + "placeholder": "Parola gücü", + "score": { + "0": "Son derece zayıf bir parola", + "1": "Çok zayıf bir parola", + "2": "Zayıf parola", + "3": "Güçlü parola", + "4": "Çok güçlü parola" + }, + "suggestion": { + "all_uppercase": "Bazı harfleri büyük harfle yazın, ancak hepsini değil.", + "another_word": "Daha az kullanılan kelimeler ekleyin.", + "associated_years": "Sizinle ilişkilendirilen yıllardan kaçının.", + "capitalization": "İlk harften başlayarak diğer harfleri de büyük harfle yazın.", + "dates": "Sizinle ilişkilendirilen tarih ve yıllardan kaçının.", + "l33t": "'a' yerine '@' gibi tahmin edilebilir harf değişikliklerinden kaçının.", + "longer_keyboard_pattern": "Daha uzun kalıplar kullanın ve yazma yönünü birden fazla kez değiştirin.", + "no_need": "Semboller, rakamlar veya büyük harfler kullanmadan da güçlü şifreler oluşturabilirsiniz.", + "pwned": "Bu parolayı başka yerlerde kullanıyorsanız, değiştirmeniz gerekir.", + "recent_years": "Son yılları göz ardı edin.", + "repeated": "Aynı kelimeleri ve karakterleri tekrar etmekten kaçının.", + "reverse_words": "Sık kullanılan kelimelerin ters yazılışlarından kaçının.", + "sequences": "Sık kullanılan karakter dizilerinden kaçının.", + "use_words": "Birden fazla kelime kullanın, ancak yaygın ifadelerden kaçının." + }, + "too_weak": "Bu parola çok zayıf.", + "warning": { + "common": "Bu, yaygın olarak kullanılan bir paroladır.", + "common_names": "Yaygın isimler ve soyadları tahmin etmek kolaydır.", + "dates": "Tarihleri ​​tahmin etmek kolaydır.", + "extended_repeat": "\"abcabcabc\" gibi tekrarlanan karakter dizileri tahmin edilmesi kolaydır.", + "key_pattern": "Kısa metin kalıplarını tahmin etmek kolaydır.", + "names_by_themselves": "Tek isim veya soyadı tahmin etmek kolaydır.", + "pwned": "Parolanız internette yaşanan bir veri ihlali sonucu ifşa edildi.", + "recent_years": "Son yılları tahmin etmek kolaydır.", + "sequences": "\"abc\" gibi yaygın karakter dizileri tahmin edilmesi kolaydır.", + "similar_to_common": "Bu, yaygın olarak kullanılan bir parolaya benziyor.", + "simple_repeat": "\"aaa\" gibi tekrarlanan karakterler tahmin edilmesi kolaydır.", + "straight_row": "Girdiğiniz harflerin düz sıralar halinde dizilmiş olması tahmin etmeyi kolaylaştırır.", + "top_hundred": "Bu, sık kullanılan bir paroladır.", + "top_ten": "Bu, çok sık kullanılan bir parola.", + "user_inputs": "Kişisel veya sayfa ile ilgili herhangi bir veri bulunmamalıdır.", + "word_by_itself": "Tek kelimeleri tahmin etmek kolaydır." + } + }, + "reset_cross_signing": { + "cancelled": { + "description_1": "Bu pencereyi kapatıp uygulamaya geri dönerek devam edebilirsiniz.", + "description_2": "Başka doğrulanmış cihazlara erişiminiz yoksa ve kurtarma anahtarınız da yoksa, uygulamayı kullanmaya devam etmek için dijital kimliğinizi sıfırlamanız gerekecektir.", + "heading": "Dijital kimlik sıfırlama işlemi iptal edildi." + }, + "description": "Başka doğrulanmış cihazlara erişiminiz yoksa ve kurtarma anahtarınız da yoksa, uygulamayı kullanmaya devam etmek için dijital kimliğinizi sıfırlamanız gerekecektir.", + "effect_list": { + "neutral_1": "Yalnızca sunucuda saklanan tüm mesaj geçmişinizi kaybedeceksiniz", + "neutral_2": "You will need to verify all your existing devices and contacts again", + "positive_1": "Hesap bilgileriniz, kişileriniz, tercihleriniz ve sohbet listeniz saklanacaktır" + }, + "failure": { + "description": "Bu geçici bir sorun olabilir, lütfen daha sonra tekrar deneyin. Sorun devam ederse, lütfen sunucu yöneticinizle iletişime geçin.", + "heading": "Dijital kimlik sıfırlama işlemi başarısız oldu" + }, + "finish_reset": "Sıfırlamayı tamamla", + "heading": "Başka bir şekilde doğrulama yapamıyorsanız dijital kimliğinizi sıfırlayın", + "start_reset": "Sıfırlamayı başlat", + "success": { + "description": "Dijital kimlik sıfırlama işlemi {{minutes}} dakika boyunca onaylanmıştır. Bu pencereyi kapatıp uygulamaya geri dönerek devam edebilirsiniz.", + "heading": "Dijital kimlik sıfırlama işlemi başarıyla oluşturuldu. İşlemi tamamlamak için uygulamaya geri dönün." + }, + "warning": "Kimliğinizi yalnızca oturum açtığınız başka bir cihaza erişiminiz yoksa ve kurtarma anahtarınızı kaybettiyseniz sıfırlayın." + }, + "session": { + "client_id_label": "Client ID", + "current": "Geçerli", + "device_id_label": "Cihaz Kimliği", + "finished_label": "Tamamlandı", + "generic_browser_session": "Tarayıcı oturumu", + "ip_label": "IP Adresi", + "last_active_label": "Son Etkinlik", + "name_for_platform": "{{name}}, {{platform}} için", + "scopes_label": "Kapsamlar", + "set_device_name": { + "help": "Bu cihazı kolayca tanımanıza yardımcı olacak bir ad belirleyin.", + "label": "Cihaz adı", + "title": "Cihaz adını düzenle" + }, + "signed_in_label": "Giriş yapıldı", + "title": "Cihaz detayları", + "unknown_browser": "Bilinmeyen tarayıcı", + "unknown_device": "Bilinmeyen cihaz", + "uri_label": "Uri" + }, + "session_detail": { + "alert": { + "button": "Geri dön", + "text": "Bu oturum mevcut değil veya artık aktif değil.", + "title": "{{deviceId}} oturumu bulunamadı" + } + }, + "user_email": { + "delete_button_confirmation_modal": { + "action": "E-postayı sil", + "body": "Bu e-postayı silmek ister misiniz?", + "incorrect_password": "Hatalı parola, lütfen tekrar deneyin", + "password_confirmation": "Bu e-posta adresini silmek için hesap parolanızı onaylayın" + }, + "delete_button_title": "E-posta adresini kaldır", + "email": "E-posta" + }, + "user_sessions_overview": { + "approaching_session_limit_warning_description:one": "{{count}} cihaz sayısının {{num_sessions}}ini kullandınız. Sınırınıza ulaştığınızda, başka bir cihaz eklemek için mevcut bir cihazı kaldırmanız gerekecektir.", + "approaching_session_limit_warning_description:other": "{{num_sessions}} veya {{count}} cihaz sayısından birini kullandınız. Sınırınıza ulaştığınızda, başka bir cihaz eklemek için mevcut bir cihazı kaldırmanız gerekecektir.", + "approaching_session_limit_warning_header": "Dikkat, cihaz limitinize yaklaşıyorsunuz", + "heading": "Oturum açtığınız yer", + "hit_session_limit_warning_description:one": "{{count}} aygıt yuvasından {{num_sessions}} aygıtını kullandınız. Tekrar oturum açmayı denediğinizde, mevcut bir aygıtı kaldırmanız gerekecektir.", + "hit_session_limit_warning_description:other": "{{num_sessions}} veya {{count}} aygıt yuvalarından birini kullandınız. Tekrar oturum açmayı denediğinizde, mevcut bir aygıtı kaldırmanız gerekecektir.", + "hit_session_limit_warning_header": "Cihaz limitine ulaştınız", + "no_active_sessions": { + "default": "Şu anda hiçbir uygulamaya giriş yapmadınız.", + "inactive_90_days": "Son 90 gün içinde tüm oturumlarınız aktif olmuştur." + }, + "num_sessions_filtered_header": { + "header:one": "Filtrenize uyan {{count}} cihaz var ({{unfiltered_session_total}})", + "header:other": "Filtrenize uyan {{count}} cihaz var ({{unfiltered_session_total}})", + "unfiltered_session_total:one": "Toplam {{count}}", + "unfiltered_session_total:other": "Toplam {{count}}" + }, + "num_sessions_header:one": "{{count}} cihaz", + "num_sessions_header:other": "{{count}} cihaz", + "session_limit_info:one": "{{count}}/{{limit}} aygıt yuvalarını kullandınız.", + "session_limit_info:other": "{{count}}/{{limit}} aygıt yuvalarını kullandınız." + }, + "verify_email": { + "code_expired_alert": { + "description": "Kodun süresi doldu. Lütfen yeni bir kod talep edin.", + "title": "Kodun süresi doldu." + }, + "code_field_error": "Kod tanınmadı", + "code_field_label": "6 haneli kod", + "code_field_wrong_shape": "Kod 6 rakamdan oluşmalıdır", + "email_sent_alert": { + "description": "Yeni kodu aşağıya girin", + "title": "Yeni kod gönderildi" + }, + "enter_code_prompt": "Gönderilen 6 haneli kodu girin: {{email}}", + "heading": "E-postanızı doğrulayın", + "invalid_code_alert": { + "description": "E-postanıza gönderilen kodu kontrol edin ve devam etmek için aşağıdaki alanları güncelleyin.", + "title": "Yanlış kod girdiniz" + }, + "resend_code": "Resend code" + } + }, + "mas": { + "scope": { + "edit_profile": "Profilinizi ve iletişim bilgilerinizi düzenleyin", + "manage_sessions": "Cihazlarınızı ve oturumlarınızı yönetin", + "mas_admin": "Kullanıcıları yönet (urn:mas:admin)", + "send_messages": "Sizin adınıza mesaj gönderin", + "synapse_admin": "Sunucuyu yönetin (urn:synapse:admin:*)", + "view_messages": "Mevcut mesajları ve verilerinizi görüntüleyinmessages", + "view_profile": "Profil bilgilerinizi ve iletişim bilgilerinizi görüntüleyin" + } + } +} \ No newline at end of file diff --git a/frontend/locales/uk.json b/frontend/locales/uk.json index 0054f18f1..9ea74cfc4 100644 --- a/frontend/locales/uk.json +++ b/frontend/locales/uk.json @@ -146,14 +146,14 @@ "current_password_label": "Поточний пароль", "failure": { "description": { - "account_locked": "Ваш обліковий запис заблоковано, і зараз його неможливо відновити. Якщо цього не передбачалося, зверніться до адміністратора сервера.", + "account_locked": "Ваш обліковий запис заблоковано, і зараз його неможливо відновити. Якщо цього не передбачалося, зверніться до постачальника вашого облікового запису.", "expired_recovery_ticket": "Посилання для відновлення застаріло. Розпочніть процес відновлення облікового запису спочатку.", "invalid_new_password": "Обраний вами новий пароль неприпустимий; він може не відповідати налаштованій політиці безпеки.", "no_current_password": "У вас немає поточного пароля.", "no_such_recovery_ticket": "Посилання для відновлення недійсне. Якщо ви скопіювали посилання з електронної пошти для відновлення, перевірте, чи скопійовано повне посилання.", "password_changes_disabled": "Зміна пароля вимкнена.", "recovery_ticket_already_used": "Посилання для відновлення вже використано. Його не можна використовувати повторно.", - "unspecified": "Це може бути тимчасова проблема, тому спробуйте пізніше. Якщо проблема не зникає, зверніться до адміністратора свого сервера.", + "unspecified": "Це може бути тимчасова проблема, тому повторіть спробу пізніше. Якщо проблема не зникне, зверніться до постачальника вашого облікового запису.", "wrong_password": "Пароль, який ви вказали як свій поточний пароль, неправильний. Спробуйте ще раз." }, "title": "Не вдалося оновити пароль" @@ -240,7 +240,7 @@ "positive_1": "Ваші дані облікового запису, контакти, налаштування та список бесід будуть збережені" }, "failure": { - "description": "Це може бути тимчасова проблема, тому спробуйте пізніше. Якщо проблема не зникає, зверніться до адміністратора свого сервера.", + "description": "Це може бути тимчасова проблема, тому повторіть спробу пізніше. Якщо проблема не зникне, зверніться до постачальника вашого облікового запису.", "heading": "Не вдалося дозволити скидання криптоідентичності" }, "finish_reset": "Завершити скидання", diff --git a/frontend/locales/uz.json b/frontend/locales/uz.json index c5969cc6d..d24ddf36f 100644 --- a/frontend/locales/uz.json +++ b/frontend/locales/uz.json @@ -4,15 +4,15 @@ "cancel": "Bekor qilish", "clear": "Tozalash", "close": "Yopish", - "collapse": "Collapse", - "confirm": "Confirm", + "collapse": "Yig‘ish", + "confirm": "Tasdiqlash", "continue": "Davom etish", "edit": "Tahrirlash", - "expand": "Expand", + "expand": "Kengaytirish", "save": "Saqlash", - "save_and_continue": "Save and continue", + "save_and_continue": "Saqlash va davom ettirish", "sign_out": "Chiqish", - "start_over": "Start over" + "start_over": "Qaytadan boshlang" }, "branding": { "privacy_policy": { @@ -25,29 +25,29 @@ } }, "common": { - "e2ee": "End-to-end encryption", + "e2ee": "Abonent shifrlash ( E2EE )", "loading": "Yuklanmoqda…", "next": "Keyingisi", "password": "Parol", "previous": "Oldingi", - "saved": "Saved", - "saving": "Saving…" + "saved": "Saqlandi", + "saving": "Saqlanmoqda…" }, "frontend": { "account": { - "account_password": "Account password", - "contact_info": "Contact info", + "account_password": "Hisob paroli", + "contact_info": "Kontakt maʼlumoti", "delete_account": { - "alert_description": "This account will be permanently erased and you’ll no longer have access to any of your messages.", - "alert_title": "You’re about to lose all of your data", - "button": "Delete account", - "dialog_description": "Confirm that you would like to delete your account:\n\n\nYou will not be able to reactivate your account\nYou will no longer be able to sign in\nNo one will be able to reuse your username (MXID), including you\nYou will leave all rooms and direct messages you are in\nYou will be removed from the identity server, and no one will be able to find you with your email or phone number\n\nYour old messages will still be visible to people who received them. Would you like to hide your sent messages from people who join rooms in the future?", - "dialog_title": "Delete this account?", - "erase_checkbox_label": "Yes, hide all my messages from new joiners", - "incorrect_password": "Incorrect password, please try again", - "mxid_label": "Confirm your Matrix ID ({{ mxid }})", - "mxid_mismatch": "This value does not match your Matrix ID", - "password_label": "Enter your password to continue" + "alert_description": "Bu hisob butunlay oʻchirib tashlanadi va siz endi xabarlaringizning birortasiga ham kira olmaysiz.", + "alert_title": "Siz barcha maʻlumotlaringizni yoʻqotish arafasidasiz", + "button": "Akkauntni o‘chirish", + "dialog_description": " Hisobingizni oʻchirishni xohlayotganingizni tasdiqlang: \n\n Hisobingizni qayta faollashtira olmaysiz \n Endi tizimga kira olmaysiz \n Hech kim, shu jumladan siz ham, foydalanuvchi nomingizni (MXID) qayta ishlata olmaydi \n Siz barcha xonalarni va toʻgridan-toʻgʻri xabarlaringizni tark etasiz \n Siz identifikatsiya serveridan oʻchirilasiz va hech kim sizni elektron pochta yoki telefon raqamingiz bilan topa olmaydi \n Eski xabarlaringiz ularni olgan odamlarga koʻrinadigan boʻladi. Kelajakda xonalarga qoʻshilgan odamlardan yuborilgan xabarlaringizni yashirishni xohlaysizmi?", + "dialog_title": "Bu akkaunt oʻchirib tashlansinmi?", + "erase_checkbox_label": "Ha, barcha xabarlarimni yangi qoʻshilganlardan yashirish", + "incorrect_password": "Parol notoʻgʻri,qaytadan urining.", + "mxid_label": "Matrix identifikatoringizni tasdiqlang ({{ mxid }})", + "mxid_mismatch": "Bu qiymat sizning Matrix ID raqamingizga mos kelmaydi", + "password_label": "Davom etish uchun parolingizni kiriting" }, "edit_profile": { "display_name_help": "Siz tizimga kirgan joyingizda boshqalar buni koʻrishadi.", @@ -61,19 +61,19 @@ "label": "Parol" }, "sign_out": { - "button": "Sign out of account", - "dialog": "Sign out of this account?" + "button": "Hisobdan chiqish", + "dialog": "Bu hisobdan chiqilsinmi?" }, "title": "Sizning hisobingiz" }, "add_email_form": { - "email_denied_error": "The entered email is not allowed by the server policy", + "email_denied_error": "Kiritilgan elektron pochta manzili server siyosati tomonidan ruxsat etilmagan", "email_field_help": "Bu hisobga kirish uchun ishlatishingiz mumkin bo‘lgan muqobil email manzilini kiriting.", "email_field_label": "Email manzilini kiritish", - "email_in_use_error": "The entered email is already in use", + "email_in_use_error": "Kiritilgan elektron pochta allaqachon ishlatilmoqda", "email_invalid_error": "Kiritilgan elektron pochta manzili notogʻri", - "incorrect_password_error": "Incorrect password, please try again", - "password_confirmation": "Confirm your account password to add this email address" + "incorrect_password_error": "Parol notoʻgʻri,qaytadan urining.", + "password_confirmation": "Ushbu elektron pochta manzilini qoʻshish uchun hisobingiz parolini tasdiqlang" }, "browser_session_details": { "current_badge": "Hozirgi" @@ -83,8 +83,8 @@ "body:other": "{{count}} ta faol seans", "heading": "Brauzerlar", "no_active_sessions": { - "default": "You are not signed in to any web browsers.", - "inactive_90_days": "All your sessions have been active in the last 90 days." + "default": "Siz hech qanday veb-brauzerga kirmagansiz.", + "inactive_90_days": "Soʻnggi 90 kun ichida barcha sessiyalaringiz faol boʻldi." }, "view_all_button": "Hammasini ko‘rish" }, @@ -99,10 +99,10 @@ "unknown": "Noma’lum qurilma turi" }, "email_in_use": { - "heading": "The email address {{email}} is already in use." + "heading": "{{email}} elektron pochta manzili allaqachon ishlatilmoqda." }, "end_session_button": { - "confirmation_modal_body_text": "Make sure you always have access to another verified device or your recovery key to avoid losing your encrypted chat history.", + "confirmation_modal_body_text": "Shifrlangan chat tarixingizni yoʻqotmaslik uchun har doim boshqa tasdiqlangan qurilmaga yoki tiklash kalitingizga kirish huquqiga ega ekanligingizga ishonch hosil qiling.", "confirmation_modal_title": "Haqiqatan ham bu qurilmani olib tashlamoqchimisiz?", "text": "Qurilmani olib tashlash" }, @@ -114,7 +114,7 @@ }, "errors": { "field_required": "Ushbu qator toʻldirilishi shart", - "rate_limit_exceeded": "You've made too many requests in a short period. Please wait a few minutes and try again." + "rate_limit_exceeded": "Qisqa vaqt ichida juda koʻp soʻrov yubordingiz. Iltimos, bir necha daqiqa kuting va qayta urinib koʻring." }, "last_active": { "active_date": "Faol {{relativeDate}}", @@ -122,9 +122,9 @@ "inactive_90_days": "90+ kun davomida faol emas" }, "nav": { - "device_limit_error": "Device limit reached", + "device_limit_error": "Qurilma limitiga yetdi", "devices": "Qurilmalar", - "plan": "Plan", + "plan": "Tarif", "settings": "Sozlamalar" }, "not_found_alert_title": "Topilmadi.", @@ -145,19 +145,19 @@ "current_password_label": "Joriy parol", "failure": { "description": { - "account_locked": "Your account is locked and can not be recovered at this time. If this is not expected, please contact your server administrator.", - "expired_recovery_ticket": "The recovery link has expired. Please start the account recovery process again from the start.", + "account_locked": "Hisobingiz qulflangan va hozirda uni qayta tiklab boʻlmaydi. Agar bu kutilmasa, iltimos, server administratoringizga murojaat qiling.", + "expired_recovery_ticket": "Tiklash havolasi muddati tugagan. Hisobni tiklash jarayonini boshidan qayta boshlang.", "invalid_new_password": "Siz tanlagan yangi parol yaroqsiz; u sozlangan xavfsizlik siyosatiga mos kelmasligi mumkin.", "no_current_password": "Sizda joriy parol yo‘q.", - "no_such_recovery_ticket": "The recovery link is invalid. If you copied the link from the recovery e-mail, please check the full link was copied.", + "no_such_recovery_ticket": "Qayta tiklash havolasi yaroqsiz. Agar siz havolani tiklash elektron pochtasidan nusxa koʻchirgan boʻlsangiz, iltimos, toʻliq havola nusxalanganligini tekshiring.", "password_changes_disabled": "Parolni o‘zgartirish faolsizlantirildi.", - "recovery_ticket_already_used": "The recovery link has already been used. It cannot be used again.", + "recovery_ticket_already_used": "Qayta tiklash havolasi allaqachon ishlatilgan. Uni qayta ishlatib boʻlmaydi.", "unspecified": "Bu vaqtinchalik muammo bo‘lishi mumkin, keyinroq qayta urining. Agar muammo bartaraf etilmasa, server administratoriga murojaat qiling.", "wrong_password": "Joriy parol sifatida kiritgan parolingiz noto‘g‘ri. Iltimos, qayta urinib ko‘ring." }, "title": "Parolni yangilab bo‘lmadi." }, - "new_password_again_label": "Enter new password again", + "new_password_again_label": "Yangi parolni qayta kiriting", "new_password_label": "Yangi parol", "passwords_match": "Parollar mos keldi!", "passwords_no_match": "Parollar mos kelmadi", @@ -170,101 +170,101 @@ }, "password_reset": { "consumed": { - "subtitle": "To create a new password, start over and select ”Forgot password“.", - "title": "The link to reset your password has already been used" + "subtitle": "Yangi parol yaratish uchun boshidan boshlang va ʼʼParolni unutdingizmiʼʼ ni tanlang.", + "title": "Parolingizni tiklash havolasi allaqachon ishlatilgan" }, "expired": { "resend_email": "Elektron pochtani qayta yuborish", - "subtitle": "Request a new email that will be sent to: {{email}}", - "title": "The link to reset your password has expired" + "subtitle": "{{email}} manziliga yuboriladigan yangi elektron pochta xabarini soʻrang", + "title": "Parolingizni tiklash havolasi muddati tugagan" }, "subtitle": "Hisobingiz uchun yangi parol tanlang.", - "title": "Reset your password" + "title": "Parolingizni qayta tiklash" }, "password_strength": { - "placeholder": "Password strength", + "placeholder": "Parol kuchi", "score": { - "0": "Extremely weak password", - "1": "Very weak password", - "2": "Weak password", - "3": "Strong password", - "4": "Very strong password" + "0": "Juda zaif parol", + "1": "Juda zaif parol", + "2": "Zaif parol", + "3": "Kuchli parol", + "4": "Juda kuchli parol" }, "suggestion": { - "all_uppercase": "Capitalise some, but not all letters.", - "another_word": "Add more words that are less common.", - "associated_years": "Avoid years that are associated with you.", - "capitalization": "Capitalise more than the first letter.", - "dates": "Avoid dates and years that are associated with you.", - "l33t": "Avoid predictable letter substitutions like '@' for 'a'.", - "longer_keyboard_pattern": "Use longer keyboard patterns and change typing direction multiple times.", - "no_need": "You can create strong passwords without using symbols, numbers, or uppercase letters.", - "pwned": "If you use this password elsewhere, you should change it.", - "recent_years": "Avoid recent years.", - "repeated": "Avoid repeated words and characters.", - "reverse_words": "Avoid reversed spellings of common words.", - "sequences": "Avoid common character sequences.", - "use_words": "Use multiple words, but avoid common phrases." + "all_uppercase": "Baʼzi harflarni bosh harf bilan yozing, lekin hammasini emas.", + "another_word": "Kamroq uchraydigan soʻzlarni qoʻshing.", + "associated_years": "Siz bilan bogʻliq yillardan qoching.", + "capitalization": "Birinchi harfdan koʻproq bosh harf bilan yozing.", + "dates": "Siz bilan bogʻliq sanalar va yillardan qoching.", + "l33t": "“a” harfi o‘rniga “@” kabi oldindan aytib bo‘ladigan harf almashtirishlardan saqlaning.", + "longer_keyboard_pattern": "Uzunroq klaviatura naqshlaridan foydalaning va yozish yoʻnalishini bir necha marta oʻzgartiring.", + "no_need": "Siz belgilar, raqamlar yoki katta harflardan foydalanmasdan kuchli parollar yaratishingiz mumkin.", + "pwned": "Agar siz ushbu parolni boshqa joyda ishlatsangiz, uni oʻzgartirishingiz kerak.", + "recent_years": "Soʻnggi yillardagi voqealardan qoching.", + "repeated": "Takrorlanadigan soʻzlar va belgilardan saqlaning.", + "reverse_words": "Umumiy soʻzlarning teskari yozilishidan saqlaning.", + "sequences": "Umumiy belgilar ketma-ketligidan qoching.", + "use_words": "Bir nechta soʻzlardan foydalaning, lekin umumiy iboralardan qoching." }, - "too_weak": "This password is too weak", + "too_weak": "Bu parol juda zaif", "warning": { - "common": "This is a commonly used password.", - "common_names": "Common names and surnames are easy to guess.", - "dates": "Dates are easy to guess.", - "extended_repeat": "Repeated character patterns like \"abcabcabc\" are easy to guess.", - "key_pattern": "Short keyboard patterns are easy to guess.", - "names_by_themselves": "Single names or surnames are easy to guess.", - "pwned": "Your password was exposed by a data breach on the Internet.", - "recent_years": "Recent years are easy to guess.", - "sequences": "Common character sequences like \"abc\" are easy to guess.", - "similar_to_common": "This is similar to a commonly used password.", - "simple_repeat": "Repeated characters like \"aaa\" are easy to guess.", - "straight_row": "Straight rows of keys on your keyboard are easy to guess.", - "top_hundred": "This is a frequently used password.", - "top_ten": "This is a heavily used password.", - "user_inputs": "There should not be any personal or page related data.", - "word_by_itself": "Single words are easy to guess." + "common": "Bu tez-tez ishlatiladigan parol.", + "common_names": "Umumiy ismlar va familiyalarni taxmin qilish oson.", + "dates": "Sanalar taxmin qilish oson.", + "extended_repeat": "\"abcabcabc\" kabi takroriy belgi naqshlarini taxmin qilish oson.", + "key_pattern": "Qisqa klaviatura birikmalarini topish oson.", + "names_by_themselves": "Bitta ism yoki familiyani taxmin qilish oson.", + "pwned": "Parolingiz internetda maʻlumotlarning oʻgʻirlanishi natijasida oshkor boʻldi.", + "recent_years": "Yillar oson topiladi.", + "sequences": "\"abc\" kabi keng tarqalgan belgilar ketma-ketligini taxmin qilish oson.", + "similar_to_common": "Bu keng tarqalgan parolga oʻxshaydi.", + "simple_repeat": "\"aaa\" kabi takroriy belgilarni taxmin qilish oson.", + "straight_row": "Klaviaturangizdagi tugmalarning toʻgʻri qatorlarini taxmin qilish oson.", + "top_hundred": "Bu tez-tez ishlatiladigan parol.", + "top_ten": "Bu juda koʻp ishlatiladigan parol.", + "user_inputs": "Shaxsiy yoki sahifa bilan bogʻliq maʻlumotlar boʻlmasligi kerak.", + "word_by_itself": "Alohida soʻzlarni taxmin qilish oson." } }, "reset_cross_signing": { "cancelled": { - "description_1": "You can close this window and go back to the app to continue.", - "description_2": "If don’t have access to any other verified devices and you don’t have your recovery key, then you’ll need to reset your digital identity to continue using the app.", - "heading": "Digital identity reset cancelled." + "description_1": "Davom etish uchun ushbu oynani yopib, ilovaga qaytishingiz mumkin.", + "description_2": "Agar boshqa tasdiqlangan qurilmalarga kirish imkoningiz boʻlmasa va sizda tiklash kaliti boʻlmasa, ilovadan foydalanishda davom etish uchun raqamli identifikatsiyangizni tiklashingiz kerak boʻladi.", + "heading": "Raqamli identifikatsiyani tiklash bekor qilindi." }, "description": "Agar boshqa tasdiqlangan qurilmalarga kirish imkoningiz boʻlmasa va sizda tiklash kaliti boʻlmasa, ilovadan foydalanishda davom etish uchun raqamli identifikatsiyangizni tiklashingiz kerak boʻladi.", "effect_list": { - "neutral_1": "You will lose any message history that's stored only on the server", + "neutral_1": "Faqat serverda saqlangan har qanday xabarlar tarixi oʻchib ketadi", "neutral_2": "You will need to verify all your existing devices and contacts again", - "positive_1": "Your account details, contacts, preferences, and chat list will be kept" + "positive_1": "Hisob maʼlumotlaringiz, kontaktlaringiz, sozlamalaringiz va suhbatlar roʻyxatingiz saqlanib qoladi" }, "failure": { "description": "Bu vaqtinchalik muammo bo‘lishi mumkin, keyinroq qayta urining. Agar muammo bartaraf etilmasa, server administratoriga murojaat qiling.", - "heading": "Failed to allow digital identity reset" + "heading": "Raqamli identifikatsiyani qayta tiklashga ruxsat berilmadi" }, - "finish_reset": "Finish reset", + "finish_reset": "Qayta tiklashni tugatish", "heading": "Boshqa usulda tasdiqlay olmasangiz, raqamli identifikatoringizni asliga qaytaring", - "start_reset": "Start reset", + "start_reset": "Qayta tiklashni boshlang", "success": { "description": "Raqamli identifikatorni tiklash keyingi {{minutes}} daqiqa uchun tasdiqlandi. Bu oynani yopib, davom etish uchun ilovaga qaytishingiz mumkin.", - "heading": "Digital identity reset successfully. Go back to the app to finish the process." + "heading": "Raqamli identifikatsiya muvaffaqiyatli tiklandi. Jarayonni yakunlash uchun ilovaga qayting." }, - "warning": "Only reset your digital identity if you don't have access to another verified device and you don't have your recovery key." + "warning": "Agar boshqa tasdiqlangan qurilmaga kira olmasangiz va zaxira kalitingiz bo‘lmasa, raqamli identifikatoringizni asliga qaytaring." }, "session": { "client_id_label": "Mijoz ID raqami", "current": "Hozirgi", "device_id_label": "Qurilma ID", "finished_label": "Tugadi", - "generic_browser_session": "Browser session", + "generic_browser_session": "Brauzer sessiyasi", "ip_label": "IP-manzil", "last_active_label": "Oxirgi faollik", "name_for_platform": "{{platform}} uchun {{name}}", "scopes_label": "Ko‘lamlar", "set_device_name": { - "help": "Set a name that will help you identify this device.", - "label": "Device name", - "title": "Edit device name" + "help": "Ushbu qurilmani aniqlashga yordam beradigan nom oʻrnating.", + "label": "Qurilma nomi", + "title": "Qurilma nomini tahrirlash" }, "signed_in_label": "Kirish", "title": "Qurilma tafsilotlari", @@ -283,39 +283,39 @@ "delete_button_confirmation_modal": { "action": "Elektron pochtani oʻchirish", "body": "Bu email o‘chirib tashlansinmi?", - "incorrect_password": "Incorrect password, please try again", - "password_confirmation": "Confirm your account password to delete this email address" + "incorrect_password": "Parol notoʻgʻri,qaytadan urining.", + "password_confirmation": "Ushbu elektron pochta manzilini oʻchirish uchun hisobingiz parolini tasdiqlang" }, "delete_button_title": "Elektron pochta manzilini olib tashlash", "email": "Elektron pochta" }, "user_sessions_overview": { - "approaching_session_limit_warning_description:one": "You've used {{num_sessions}} of {{count}} device slot. Once you reach your limit, you'll need to remove an existing device to add another.", - "approaching_session_limit_warning_description:other": "You've used {{num_sessions}} of {{count}} device slots. Once you reach your limit, you'll need to remove an existing device to add another.", - "approaching_session_limit_warning_header": "Heads up, you're close to your device limit", + "approaching_session_limit_warning_description:one": "Siz {{count}} qurilma uyasidan {{num_sessions}} dan foydalandingiz. Cheklovga yetganingizdan soʻng, yangi qurilma qoʻshish uchun mavjud qurilmadan birini olib tashlashingiz kerak boʻladi.", + "approaching_session_limit_warning_description:other": "Siz {{count}} qurilma uyalaridan {{num_sessions}} dan foydalangansiz. Cheklovga yetganingizdan soʻng, boshqa qurilma qoʻshish uchun mavjud qurilmadan birini olib tashlashingiz kerak boʻladi.", + "approaching_session_limit_warning_header": "Diqqat! Qurilmangiz limitiga yaqinlashdingiz", "heading": "Qayerda tizimga kirgansiz", - "hit_session_limit_warning_description:one": "You've used {{num_sessions}} of {{count}} device slot. When you try to sign-in again, you'll need to remove an existing device.", - "hit_session_limit_warning_description:other": "You've used {{num_sessions}} of {{count}} device slots. When you try to sign-in again, you'll need to remove an existing device.", - "hit_session_limit_warning_header": "You've hit the device limit", + "hit_session_limit_warning_description:one": "Siz {{count}} qurilma uyasidan {{num_sessions}} dan foydalandingiz. Qayta kirishga urinayotganingizda, mavjud qurilmani olib tashlashingiz kerak boʻladi.", + "hit_session_limit_warning_description:other": "Siz {{count}} qurilma uyalaridan {{num_sessions}} dan foydalangansiz. Qayta kirishga urinayotganingizda, mavjud qurilmani olib tashlashingiz kerak boʻladi.", + "hit_session_limit_warning_header": "Qurilmalar soni limitiga yetdingiz", "no_active_sessions": { - "default": "You are not signed in to any application.", - "inactive_90_days": "All your sessions have been active in the last 90 days." + "default": "Siz hech qanday ilovaga kirmagansiz.", + "inactive_90_days": "Soʻnggi 90 kun ichida barcha sessiyalaringiz faol boʻldi." }, "num_sessions_filtered_header": { - "header:one": "{{count}} device matches your filter ({{unfiltered_session_total}})", - "header:other": "{{count}} devices match your filter ({{unfiltered_session_total}})", - "unfiltered_session_total:one": "{{count}} total", - "unfiltered_session_total:other": "{{count}} total" + "header:one": "{{count}} qurilmasi sizning filtringizga mos keladi ({{unfiltered_session_total}})", + "header:other": "{{count}} qurilmalari sizning filtringizga mos keladi ({{unfiltered_session_total}})", + "unfiltered_session_total:one": "Jami {{count}}", + "unfiltered_session_total:other": "Jami {{count}}" }, - "num_sessions_header:one": "{{count}} device", - "num_sessions_header:other": "{{count}} devices", - "session_limit_info:one": "You've used {{count}}/{{limit}} device slots", - "session_limit_info:other": "You've used {{count}}/{{limit}} device slots" + "num_sessions_header:one": "{{count}} qurilmasi", + "num_sessions_header:other": "{{count}} qurilmalari", + "session_limit_info:one": "Siz {{count}}/{{limit}} qurilma uyalaridan foydalangansiz", + "session_limit_info:other": "Siz {{count}}/{{limit}} qurilma uyalaridan foydalangansiz" }, "verify_email": { "code_expired_alert": { - "description": "The code has expired. Please request a new code.", - "title": "Code expired" + "description": "Kodning amal qilish muddati tugagan. Iltimos, yangi kod soʻrang.", + "title": "Kod muddati tugagan" }, "code_field_error": "Kod tan olinmadi", "code_field_label": "6 xonali kod", diff --git a/frontend/locales/zh-Hans.json b/frontend/locales/zh-Hans.json index 3e6357f2d..aadf2fd63 100644 --- a/frontend/locales/zh-Hans.json +++ b/frontend/locales/zh-Hans.json @@ -116,9 +116,9 @@ "rate_limit_exceeded": "你在短时间内发出了过多请求。请于几分钟后重试。" }, "last_active": { - "active_date": "活跃 {{relativeDate}}", + "active_date": "活跃于 {{relativeDate}}", "active_now": "活跃", - "inactive_90_days": "已停用90天以上" + "inactive_90_days": "已停用 90 天以上" }, "nav": { "device_limit_error": "已达到设备数量上限", diff --git a/frontend/src/entrypoints/form-post.ts b/frontend/src/entrypoints/form-post.ts new file mode 100644 index 000000000..fe52c04cb --- /dev/null +++ b/frontend/src/entrypoints/form-post.ts @@ -0,0 +1,10 @@ +// Copyright 2026 Element Creations Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +// Please see LICENSE files in the repository root for full details. + +// Submit the form on the next tick, so the browser can paint the +// placeholder before navigating away. +setTimeout(() => { + document.forms[0].submit(); +}, 0); diff --git a/frontend/src/entrypoints/swagger-oauth2-redirect.ts b/frontend/src/entrypoints/swagger-oauth2-redirect.ts new file mode 100644 index 000000000..9eb1153d7 --- /dev/null +++ b/frontend/src/entrypoints/swagger-oauth2-redirect.ts @@ -0,0 +1,104 @@ +// Copyright 2026 Element Creations Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +// Please see LICENSE files in the repository root for full details. + +// This is taken from the swagger-ui/dist/oauth2-redirect.html file + +interface SwaggerUIRedirectOauth2 { + state?: string; + redirectUrl: string; + auth: { + name: string; + code?: string; + schema: { get(key: string): string }; + }; + errCb: (error: { + authId: string; + source: string; + level: string; + message: string; + }) => void; + callback: (result: { + auth: SwaggerUIRedirectOauth2["auth"]; + redirectUrl: string; + token?: unknown; + isValid?: boolean; + }) => void; +} + +interface RedirectOpener extends Window { + swaggerUIRedirectOauth2: SwaggerUIRedirectOauth2; +} + +const oauth2 = (window.opener as RedirectOpener).swaggerUIRedirectOauth2; +const sentState = oauth2.state; +const redirectUrl = oauth2.redirectUrl; + +let qpString: string; +if (/code|token|error/.test(window.location.hash)) { + qpString = window.location.hash.substring(1).replace("?", "&"); +} else { + qpString = location.search.substring(1); +} + +const arr = qpString.split("&"); +for (let i = 0; i < arr.length; i++) { + arr[i] = `"${arr[i].replace("=", '":"')}"`; +} +const qp: Record = qpString + ? JSON.parse(`{${arr.join()}}`, (key, value) => + key === "" ? value : decodeURIComponent(value), + ) + : {}; + +const isValid = qp.state === sentState; + +if ( + (oauth2.auth.schema.get("flow") === "accessCode" || + oauth2.auth.schema.get("flow") === "authorizationCode" || + oauth2.auth.schema.get("flow") === "authorization_code") && + !oauth2.auth.code +) { + if (!isValid) { + oauth2.errCb({ + authId: oauth2.auth.name, + source: "auth", + level: "warning", + message: + "Authorization may be unsafe, passed state was changed in server. The passed state wasn't returned from auth server.", + }); + } + + if (qp.code) { + delete oauth2.state; + oauth2.auth.code = qp.code; + oauth2.callback({ auth: oauth2.auth, redirectUrl: redirectUrl }); + } else { + let oauthErrorMsg: string | undefined; + if (qp.error) { + oauthErrorMsg = `[${qp.error}]: ${ + qp.error_description + ? `${qp.error_description}. ` + : "no accessCode received from the server. " + }${qp.error_uri ? `More info: ${qp.error_uri}` : ""}`; + } + + oauth2.errCb({ + authId: oauth2.auth.name, + source: "auth", + level: "error", + message: + oauthErrorMsg || + "[Authorization failed]: no accessCode received from the server.", + }); + } +} else { + oauth2.callback({ + auth: oauth2.auth, + token: qp, + isValid: isValid, + redirectUrl: redirectUrl, + }); +} +window.close(); diff --git a/misc/build-docs.sh b/misc/build-docs.sh index 390c1376f..ee9c08a6f 100644 --- a/misc/build-docs.sh +++ b/misc/build-docs.sh @@ -1,5 +1,6 @@ #!/bin/sh +# Copyright 2025, 2026 Element Creations Ltd. # Copyright 2025 New Vector Ltd. # # SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial @@ -22,14 +23,15 @@ if [ "${CF_PAGES:-""}" = "1" ]; then # Source the environment variables to add cargo to the path . "$HOME/.cargo/env" - # Install the minimal toolchain, which includes rustc, rustdoc, and cargo - rustup toolchain install stable --profile minimal + # Install the toolchain pinned in rust-toolchain.toml + rustup toolchain install # Install mdbook MDBOOK_URL="https://github.com/rust-lang/mdBook/releases/download/v${MDBOOK_VERSION}/mdbook-v${MDBOOK_VERSION}-$(uname -m)-unknown-linux-gnu.tar.gz" curl --proto '=https' --tlsv1.2 -sSfL "${MDBOOK_URL}" | tar -C "$HOME/.cargo/bin" -xzv - # Enable pnpm via corepack (Node and corepack are pre-installed on Cloudflare Pages) + # Enable pnpm via corepack (Node and corepack are pre-installed on Cloudflare + # Pages, which picks the Node version from .node-version) corepack enable fi @@ -55,5 +57,7 @@ rm -rf target/book/rustdoc mv target/doc target/book/rustdoc # Build the frontend storybook -pnpm install --frozen-lockfile +# Node is already provided by Cloudflare Pages / the CI runner, so skip pnpm's +# own runtime download. +pnpm install --frozen-lockfile --no-runtime pnpm --filter mas-frontend exec storybook build -o ../target/book/storybook diff --git a/package.json b/package.json index 563c22fa7..ef74b844d 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,13 @@ "name": "matrix-authentication-service", "private": true, "packageManager": "pnpm@11.3.0+sha512.2c403d6594527287672b1f7056343a1f7c3634036a67ffabfcc2b3d7595d843768f8787148d1b57cf7956c90606bbd192857c363af19e96d2d0ec9ec5741d215", + "devEngines": { + "runtime": { + "name": "node", + "version": "24.15.0", + "onFail": "download" + } + }, "devDependencies": { "@localazy/cli": "^2.0.11", "semver": "^7.8.5" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f3e45e171..76da741a6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,6 +14,9 @@ importers: '@localazy/cli': specifier: ^2.0.11 version: 2.0.11 + node: + specifier: runtime:24.15.0 + version: runtime:24.15.0 semver: specifier: ^7.8.5 version: 7.8.5 @@ -3971,6 +3974,127 @@ packages: resolution: {integrity: sha512-MRlTqhAfoMx/4mhEbPo3Hi02g9LJZaJkka69V6h67Cb1gjrAG0jsTE4CZX1eptNx+VCAwJmfpnDIF4P0Nh1A7A==} engines: {node: '>=18'} + node@runtime:24.15.0: + resolution: + type: variations + variants: + - resolution: + archive: tarball + bin: + node: bin/node + integrity: sha256-3UvHfctfTJoslkNzm7WTUAzLCUE+xCyqD47w5e8RYJU= + type: binary + url: https://nodejs.org/download/release/v24.15.0/node-v24.15.0-aix-ppc64.tar.gz + targets: + - cpu: ppc64 + os: aix + - resolution: + archive: tarball + bin: + node: bin/node + integrity: sha256-NyMxuWl3mrXRW5SYhPxur4jVr+h73ouogdZAC5EA/8Q= + type: binary + url: https://nodejs.org/download/release/v24.15.0/node-v24.15.0-darwin-arm64.tar.gz + targets: + - cpu: arm64 + os: darwin + - resolution: + archive: tarball + bin: + node: bin/node + integrity: sha256-/9XuKTRnkn8+5zGlU+uI/R9Iz3TuvC10prq+SvIoZzs= + type: binary + url: https://nodejs.org/download/release/v24.15.0/node-v24.15.0-darwin-x64.tar.gz + targets: + - cpu: x64 + os: darwin + - resolution: + archive: tarball + bin: + node: bin/node + integrity: sha256-c6/CNNVYwkkZh19RwtHqACoq2k6m+DYBo4OGn++mTu0= + type: binary + url: https://nodejs.org/download/release/v24.15.0/node-v24.15.0-linux-arm64.tar.gz + targets: + - cpu: arm64 + os: linux + - resolution: + archive: tarball + bin: + node: bin/node + integrity: sha256-sfiJAKSxY2XqulYmkwT8Edp1gdvwNVLUSV+azh/AX20= + type: binary + url: https://nodejs.org/download/release/v24.15.0/node-v24.15.0-linux-ppc64le.tar.gz + targets: + - cpu: ppc64le + os: linux + - resolution: + archive: tarball + bin: + node: bin/node + integrity: sha256-+YVFVDnVL+m43mqPbQe/7MxzbepSfofqyv5ajXUWo4A= + type: binary + url: https://nodejs.org/download/release/v24.15.0/node-v24.15.0-linux-s390x.tar.gz + targets: + - cpu: s390x + os: linux + - resolution: + archive: tarball + bin: + node: bin/node + integrity: sha256-RINoctmuxJ8ea1KpqSKHLbmisC0jWmFqVoG2qF/sjYk= + type: binary + url: https://nodejs.org/download/release/v24.15.0/node-v24.15.0-linux-x64.tar.gz + targets: + - cpu: x64 + os: linux + - resolution: + archive: zip + bin: + node: node.exe + integrity: sha256-yet0Au2ibiun5EtnJ/yFqN5WxQlbH3Hr0wYokiEaoRY= + prefix: node-v24.15.0-win-arm64 + type: binary + url: https://nodejs.org/download/release/v24.15.0/node-v24.15.0-win-arm64.zip + targets: + - cpu: arm64 + os: win32 + - resolution: + archive: zip + bin: + node: node.exe + integrity: sha256-zFFJ6r1Td5zh573FQBZDYi0MfmgAreGJKKdn6UC7DmI= + prefix: node-v24.15.0-win-x64 + type: binary + url: https://nodejs.org/download/release/v24.15.0/node-v24.15.0-win-x64.zip + targets: + - cpu: x64 + os: win32 + - resolution: + archive: tarball + bin: + node: bin/node + integrity: sha256-MemKqWCgZ9qR7f/V2TvEZle10qgClhLDWfXyrABgFSo= + type: binary + url: https://unofficial-builds.nodejs.org/download/release/v24.15.0/node-v24.15.0-linux-arm64-musl.tar.gz + targets: + - cpu: arm64 + os: linux + libc: musl + - resolution: + archive: tarball + bin: + node: bin/node + integrity: sha256-9Vr1vUicU0exE8pllMrgClSzC6V6xYdTJDEb/G9HYuM= + type: binary + url: https://unofficial-builds.nodejs.org/download/release/v24.15.0/node-v24.15.0-linux-x64-musl.tar.gz + targets: + - cpu: x64 + os: linux + libc: musl + version: 24.15.0 + hasBin: true + normalize-path@2.1.1: resolution: {integrity: sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==} engines: {node: '>=0.10.0'} @@ -8411,6 +8535,8 @@ snapshots: node-releases@2.0.52: {} + node@runtime:24.15.0: {} + normalize-path@2.1.1: dependencies: remove-trailing-separator: 1.1.0 diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 000000000..c0b6afb91 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,11 @@ +# Copyright 2026 Element Creations Ltd. +# +# SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +# Please see LICENSE files in the repository root for full details. + +[toolchain] +channel = "1.96.0" +profile = "minimal" +# No rustfmt: .rustfmt.toml uses nightly-only options, so formatting runs as `cargo +nightly fmt`. +# No cross-compilation targets: the Dockerfile and build.yaml add the ones they need. +components = ["clippy"] diff --git a/templates/components/back_to_client.html b/templates/components/back_to_client.html index 67417584d..97605ed0d 100644 --- a/templates/components/back_to_client.html +++ b/templates/components/back_to_client.html @@ -25,7 +25,9 @@ Please see LICENSE files in the repository root for full details. {% if mode == "form_post" %}
{% for key, value in params|items %} - + {% if value is not none and value is defined %} + + {% endif %} {% endfor %}
diff --git a/templates/form_post.html b/templates/form_post.html index 71882c8ab..253c152d4 100644 --- a/templates/form_post.html +++ b/templates/form_post.html @@ -1,4 +1,5 @@ {# +Copyright 2025, 2026 Element Creations Ltd. Copyright 2024, 2025 New Vector Ltd. Copyright 2021-2024 The Matrix.org Foundation C.I.C. @@ -28,5 +29,5 @@ Please see LICENSE files in the repository root for full details. {# Submit the form in JavaScript on the next tick, so that if the browser wants to display the placeholder instead of a blank page, it can #} - + {{ include_asset('src/entrypoints/form-post.ts') | indent(2) | safe }} {% endblock %} diff --git a/templates/pages/compat_login_policy_violation.html b/templates/pages/compat_login_policy_violation.html index 2c1a12847..d6b7372dc 100644 --- a/templates/pages/compat_login_policy_violation.html +++ b/templates/pages/compat_login_policy_violation.html @@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details. {% extends "base.html" %} -{% set is_too_many_sessions = violations | length == 1 and violations[0].code == "too-many-sessions" %} +{% set is_too_many_sessions = violations | length == 1 and violations[0].code | default("") == "too-many-sessions" %} {% set num_sessions_need_to_remove = violations[0].need_to_remove if is_too_many_sessions else none %} {% block content %} diff --git a/templates/pages/policy_violation.html b/templates/pages/policy_violation.html index 2b97a8bc4..d44593c85 100644 --- a/templates/pages/policy_violation.html +++ b/templates/pages/policy_violation.html @@ -8,7 +8,7 @@ Please see LICENSE files in the repository root for full details. {% extends "base.html" %} -{% set is_too_many_sessions = violations | length == 1 and violations[0].code == "too-many-sessions" %} +{% set is_too_many_sessions = violations | length == 1 and violations[0].code | default("") == "too-many-sessions" %} {% set num_sessions_need_to_remove = violations[0].need_to_remove if is_too_many_sessions else none %} {% block content %} diff --git a/templates/swagger/oauth2-redirect.html b/templates/swagger/oauth2-redirect.html index 831e518ec..d9a056d3a 100644 --- a/templates/swagger/oauth2-redirect.html +++ b/templates/swagger/oauth2-redirect.html @@ -1,4 +1,5 @@ {# +Copyright 2025, 2026 Element Creations Ltd. Copyright 2024, 2025 New Vector Ltd. Copyright 2024 The Matrix.org Foundation C.I.C. @@ -12,78 +13,7 @@ Please see LICENSE files in the repository root for full details. API documentation: OAuth2 Redirect + {{ include_asset('src/entrypoints/swagger-oauth2-redirect.ts') | indent(4) | safe }} - - - + diff --git a/translations/de.json b/translations/de.json index fe1182697..29f2099dc 100644 --- a/translations/de.json +++ b/translations/de.json @@ -7,7 +7,7 @@ "sign_in": "Anmelden", "sign_out": "Abmelden", "skip": "Überspringen", - "start_over": "Von vorne anfangen" + "start_over": "Neu beginnen" }, "app": { "human_name": "Matrix Authentication Service", @@ -27,11 +27,11 @@ "common": { "display_name": "Anzeigename", "email_address": "E-Mail-Adresse", - "loading": "Lade …", + "loading": "Wird geladen …", "mxid": "Matrix-ID", "password": "Passwort", - "password_confirm": "Passwort wiederholen", - "username": "Benutzername" + "password_confirm": "Passwort bestätigen", + "username": "Nutzername" }, "error": { "unexpected": "Unerwarteter Fehler" @@ -39,11 +39,11 @@ "mas": { "account": { "deactivated": { - "description": "Dieses Konto (%(mxid)s) wurde entfernt. Wenn du dies nicht erwartet hast, wende dich an den Server Admin", + "description": "Dieses Konto (%(mxid)s) wurde entfernt. Wenn du dies nicht erwartet hast, wende dich an den Kontoanbieter.", "heading": "Konto gelöscht" }, "locked": { - "description": "Dieses Konto (%(mxid)s) wurde gesperrt. Wenn du dies nicht erwartet hast, wende dich an den Server Admin.", + "description": "Dieses Konto (%(mxid)s) wurde gesperrt. Wenn du dies nicht erwartet hast, wende dich an den Kontoanbieter.", "heading": "Konto gesperrt" }, "logged_out": { @@ -57,7 +57,7 @@ }, "change_password": { "change": "Passwort ändern", - "confirm": "Passwort wiederholen", + "confirm": "Passwort bestätigen", "current": "Aktuelles Passwort", "heading": "Mein Passwort ändern", "new": "Neues Passwort" @@ -79,8 +79,8 @@ "security_code": "Sicherheitscode" }, "device_code_link": { - "description": "Eingeben Sie den Sicherheitscode ein, der auf Ihrem anderen Gerät angezeigt wird", - "headline": "Gib den Code ein, der auf deinem Gerät angezeigt wird.", + "description": "Gib den Sicherheitscode ein, der auf deinem anderen Gerät angezeigt wird", + "headline": "Gerät zu deinem Konto hinzufügen", "verification_code": "Bestätigungscode" }, "device_consent": { @@ -113,10 +113,10 @@ "emails": { "greeting": "Hallo %(username)s,", "recovery": { - "click_button": "Klick unten, um ein neues Passwort zu erstellen:", + "click_button": "Wähle die Schaltfläche unten, um ein neues Passwort zu erstellen:", "copy_link": "Kopier den folgenden Link und füge ihn in deinen Browser ein, um ein neues Passwort zu erstellen:", "create_new_password": "Neues Passwort erstellen", - "fallback": "Funktioniert der Button bei Ihnen nicht?", + "fallback": "Funktioniert die Schaltfläche bei dir nicht?", "headline": "Du hast das Zurücksetzen des Passworts für dein Konto bei %(server_name)s angefordert.", "subject": "Setze dein Konto Passwort zurück (%(mxid)s)", "you_can_ignore": "Wenn du kein neues Passwort angefordert hast, kannst du diese E-Mail einfach ignorieren. Dein aktuelles Passwort funktioniert weiterhin." @@ -128,7 +128,7 @@ } }, "errors": { - "captcha": "Die CAPTCHA-Überprüfung hat nicht geklappt, versuch's nochmal.", + "captcha": "Die CAPTCHA-Überprüfung ist fehlgeschlagen, bitte versuche es erneut", "denied_policy": "Abgelehnt durch Richtlinie: %(policy)s", "email_banned": "E-Mail ist durch die Serverrichtlinie gesperrt", "email_domain_banned": "Die E-Mail-Domain ist durch die Serverrichtlinie gesperrt", @@ -137,14 +137,14 @@ "field_required": "Dieses Feld ist ein Pflichtfeld", "invalid_credentials": "Ungültige Anmeldeinformationen", "password_mismatch": "Die Passwortfelder stimmen nicht überein", - "rate_limit_exceeded": "Du hast in kurzer Zeit zu viele Anfragen gestellt. Warte bitte ein paar Minuten und versuch's nochmal.", - "username_all_numeric": "Der Benutzername darf nicht nur aus Zahlen bestehen", - "username_banned": "Der Benutzername ist durch die Serverrichtlinie gesperrt", + "rate_limit_exceeded": "Du hast in kurzer Zeit zu viele Anfragen gestellt. Bitte warte ein paar Minuten und versuche es erneut.", + "username_all_numeric": "Der Nutzername darf nicht nur aus Zahlen bestehen", + "username_banned": "Der Nutzername ist durch die Serverrichtlinie gesperrt", "username_invalid_chars": "Der Nutzername enthält ungültige Zeichen. Verwende nur Kleinbuchstaben, Zahlen, Bindestriche und Unterstriche.", - "username_not_allowed": "Der Benutzername ist gemäß der Serverrichtlinie nicht zulässig", - "username_taken": "Dieser Benutzername ist bereits vergeben", - "username_too_long": "Der Benutzername ist zu lang", - "username_too_short": "Der Benutzername ist zu kurz" + "username_not_allowed": "Der Nutzername ist gemäß der Serverrichtlinie nicht zulässig", + "username_taken": "Dieser Nutzername ist bereits vergeben", + "username_too_long": "Der Nutzername ist zu lang", + "username_too_short": "Der Nutzername ist zu kurz" }, "legacy_consent": { "this_will_setup": "Dadurch wird %(client_name)s mit deinem %(server_name)s Konto eingerichtet." @@ -160,7 +160,7 @@ "headline": "Melde dich zum Verbinden an" }, "no_login_methods": "Keine Anmeldemethoden verfügbar.", - "username_or_email": "Benutzername oder E-Mail-Adresse" + "username_or_email": "Nutzername oder E-Mail-Adresse" }, "navbar": { "my_account": "Mein Konto", @@ -168,13 +168,13 @@ "signed_in_as": "Angemeldet als %(username)s." }, "not_found": { - "description": "Die Seite, die du gesucht hast, gibt's nicht oder wurde verschoben.", + "description": "Die Seite, die du gesucht hast, existiert nicht oder wurde verschoben", "heading": "Seite nicht gefunden" }, "not_you": "Nicht %(username)s?", "or_separator": "Oder", "policy_violation": { - "description": "Dies kann am Client liegen, der die Anfrage erstellt hat, am aktuell angemeldeten Benutzer oder an der Anfrage selbst.", + "description": "Dies kann an der Anwendung liegen, die die Anfrage erstellt hat, am aktuell angemeldeten Nutzer oder an der Anfrage selbst.", "heading": "Die Autorisierungsanfrage wurde durch eine Richtlinie dieses Dienstes abgelehnt", "logged_as": "Eingeloggt als %(username)s", "too_many_sessions": { @@ -185,7 +185,7 @@ }, "recovery": { "consumed": { - "description": "Um ein neues Passwort zu erstellen, fang einfach von vorne an und wähle „Passwort vergessen”.", + "description": "Um ein neues Passwort zu erstellen, beginne neu und wähle „Passwort vergessen“.", "heading": "Der Link zum Zurücksetzen deines Passworts wurde bereits verwendet" }, "disabled": { @@ -205,7 +205,7 @@ "save_and_continue": "Speichern und fortfahren" }, "progress": { - "change_email": "Probier's mal mit einer anderen E-Mail-Adresse", + "change_email": "Andere E-Mail-Adresse versuchen", "description": "Eine E-Mail mit einem Link zum Zurücksetzen deines Passworts wurde versendet, wenn es ein Konto mit %(email)s gibt.", "heading": "Überprüfe deine E-Mails!", "resend_email": "E-Mail erneut senden" @@ -223,17 +223,17 @@ "description": "Wähle einen Nutzernamen, um fortzufahren.", "heading": "Konto erstellen" }, - "terms_of_service": "Ich stimme den Allgemeinen Geschäftsbedingungen zu." + "terms_of_service": "Ich stimme den Allgemeinen Geschäftsbedingungen zu" }, "registration_token": { - "description": "Gib das Registrierungstoken ein, das du vom Homeserver-Admin bekommen hast.", + "description": "Gib ein Registrierungstoken ein, das du von deinem Kontoanbieter erhalten hast.", "field": "Registrierungstoken", "headline": "Registrierungstoken" }, "scope": { "edit_profile": "Bearbeite dein Profil und deine Kontaktdaten", "manage_sessions": "Verwalte deine Geräte und Sitzungen", - "mas_admin": "Beliebige Benutzer verwalten (urn:mas:admin)", + "mas_admin": "Beliebige Nutzer verwalten (urn:mas:admin)", "send_messages": "Neue Nachrichten in deinem Namen senden", "synapse_admin": "Den Synapse-Homeserver verwalten (urn:synapse:admin:*)", "view_messages": "Zeig deine vorhandenen Nachrichten und Daten an", @@ -252,7 +252,7 @@ "enforced_by_policy": "Erzwungen durch Server-Richtlinie", "forced_display_name": "Verwendet den folgenden Anzeigenamen", "forced_email": "Verwendet die folgende E-Mail-Adresse", - "forced_localpart": "Verwendet den folgenden Benutzernamen", + "forced_localpart": "Verwendet den folgenden Nutzernamen", "import_data": { "description": "Bestätige die Informationen, die mit dem neuen %(server_name)s Konto verknüpft werden.", "heading": "Deine Daten importieren" @@ -262,7 +262,7 @@ "link_existing": "Mit einem bestehenden Konto verknüpfen", "provider_name": "%(human_name)s Konto", "signup_with_upstream": { - "heading": "Setze die Anmeldung mit %(human_name)s Konto fort." + "heading": "Setze die Anmeldung mit %(human_name)s Konto fort" }, "suggested_display_name": "Anzeigenamen importieren", "suggested_email": "E-Mail-Adresse importieren", diff --git a/translations/en.json b/translations/en.json index c0b1ecad8..e7df4b636 100644 --- a/translations/en.json +++ b/translations/en.json @@ -10,7 +10,7 @@ }, "continue": "Continue", "@continue": { - "context": "form_post.html:25:28-48, pages/consent.html:71:28-48, pages/device_link.html:42:28-48, pages/login.html:68:30-50, pages/reauth.html:32:28-48, pages/recovery/start.html:38:26-46, pages/register/password.html:80:26-46, pages/register/steps/display_name.html:43:28-48, pages/register/steps/registration_token.html:41:28-48, pages/register/steps/verify_email.html:51:26-46, pages/sso.html:52:28-48" + "context": "form_post.html:26:28-48, pages/consent.html:71:28-48, pages/device_link.html:42:28-48, pages/login.html:68:30-50, pages/reauth.html:32:28-48, pages/recovery/start.html:38:26-46, pages/register/password.html:80:26-46, pages/register/steps/display_name.html:43:28-48, pages/register/steps/registration_token.html:41:28-48, pages/register/steps/verify_email.html:51:26-46, pages/sso.html:52:28-48" }, "create_account": "Create Account", "@create_account": { @@ -83,7 +83,7 @@ }, "loading": "Loading…", "@loading": { - "context": "form_post.html:14:27-46" + "context": "form_post.html:15:27-46" }, "mxid": "Matrix ID", "@mxid": { diff --git a/translations/et.json b/translations/et.json index 016c6cc4c..c24030796 100644 --- a/translations/et.json +++ b/translations/et.json @@ -39,11 +39,11 @@ "mas": { "account": { "deactivated": { - "description": "See kasutajakonto (%(mxid)s) on kustutatud. Kui see nii ei peaks olema, siis palun võta ühendust oma serveri haldajaga.", + "description": "See kasutajakonto (%(mxid)s) on kustutatud. Kui see nii ei peaks olema, siis palun võta ühendust oma teenusepakkujaga.", "heading": "Kasutajakonto on kustutatud" }, "locked": { - "description": "See kasutajakonto (%(mxid)s) on lukustatud. Kui see nii ei peaks olema, siis palun võta ühendust oma serveri haldajaga.", + "description": "See kasutajakonto (%(mxid)s) on lukustatud. Kui see nii ei peaks olema, siis palun võta ühendust oma teenusepakkujaga.", "heading": "Kasutajakonto on lukustatud" }, "logged_out": { @@ -226,7 +226,7 @@ "terms_of_service": "Ma nõustun teenuse kasutustingimustega" }, "registration_token": { - "description": "Palun sisesta registreerimise tunnusluba, mille serveri haldaja on sulle andnud.", + "description": "Palun sisesta registreerimise tunnusluba, mille sinu teenusepakkuja on sulle andnud.", "field": "Registreerimise tunnusluba", "headline": "Registreerimise tunnusluba" }, diff --git a/translations/fr.json b/translations/fr.json index af95ed90f..281c8f7ef 100644 --- a/translations/fr.json +++ b/translations/fr.json @@ -79,8 +79,8 @@ "security_code": "Code de sécurité" }, "device_code_link": { - "description": "Associer un appareil", - "headline": "Entrez le code affiché sur votre appareil", + "description": "Saisissez le code de sécurité affiché sur votre autre appareil", + "headline": "Associer un nouvel appareil à votre compte", "verification_code": "Code de vérification" }, "device_consent": { diff --git a/translations/pl.json b/translations/pl.json index 4964b924b..f46a12dc7 100644 --- a/translations/pl.json +++ b/translations/pl.json @@ -39,11 +39,11 @@ "mas": { "account": { "deactivated": { - "description": "To konto (%(mxid)s) zostało usunięte. Jeśli jest to nieoczekiwane, skontaktuj się z administratorem serwera.", + "description": "To konto (%(mxid)s) zostało usunięte. Jeśli jest to nieoczekiwane, skontaktuj się z dostawcą konta.", "heading": "Konto usunięte" }, "locked": { - "description": "To konto (%(mxid)s) zostało zablokowane. Jeśli jest to nieoczekiwane, skontaktuj się z administratorem serwera.", + "description": "To konto (%(mxid)s) zostało zablokowane. Jeśli jest to nieoczekiwane, skontaktuj się z dostawcą konta.", "heading": "Konto zablokowane" }, "logged_out": { @@ -226,7 +226,7 @@ "terms_of_service": "Wyrażam zgodę na warunki korzystania z serwisu" }, "registration_token": { - "description": "Wprowadź token rejestracyjny dostarczony przez administratora serwera domowego.", + "description": "Wprowadź token rejestracyjny dostarczony przez dostawcę Twojego konta.", "field": "Token rejestracyjny", "headline": "Token rejestracyjny" }, @@ -234,10 +234,10 @@ "edit_profile": "Edytuj swój profil i dane kontaktowe", "manage_sessions": "Zarządzaj swoimi urządzeniami i sesjami", "mas_admin": "Zarządzaj użytkownikami (urn:mas:admin)", - "send_messages": "Wysyłaj nowe wiadomości w Twoim imieniu", + "send_messages": "Wysyłanie nowych wiadomości w Twoim imieniu", "synapse_admin": "Administrowanie serwerem (urn:synapse:admin:*)", - "view_messages": "Przegląd istniejących wiadomości i danych", - "view_profile": "Przegląd informacji o profilu i danych kontaktowych" + "view_messages": "Przeglądanie istniejących wiadomości i danych", + "view_profile": "Przeglądanie informacji profilowych i danych kontaktowych" }, "upstream_oauth2": { "link_mismatch": { diff --git a/translations/tr.json b/translations/tr.json new file mode 100644 index 000000000..67e75e028 --- /dev/null +++ b/translations/tr.json @@ -0,0 +1,277 @@ +{ + "action": { + "back": "Geri", + "cancel": "İptal", + "continue": "Devam Et", + "create_account": "Hesap Oluştur", + "sign_in": "Giriş Yap", + "sign_out": "Çıkış Yap", + "skip": "Atla", + "start_over": "Baştan başla" + }, + "app": { + "human_name": "Matrix Authentication Service", + "name": "matrix-authentication-service", + "technical_description": "OpenID Connect keşif belgesi: %(discovery_url)s" + }, + "branding": { + "privacy_policy": { + "alt": "Hizmet gizlilik politikasına bağlantı", + "link": "Gizlilik Politikası" + }, + "terms_and_conditions": { + "alt": "Hizmet şartları ve koşullarına bağlantı", + "link": "Şartlar & Koşullar" + } + }, + "common": { + "email_address": "E-posta Adresi", + "loading": "Yükleniyor…", + "mxid": "Matrix Kimliği", + "password": "Şifre", + "password_confirm": "Şifreyi onayla", + "username": "Kullanıcı Adı" + }, + "error": { + "unexpected": "Beklenmeyen hata" + }, + "mas": { + "account": { + "deactivated": { + "description": "Bu hesap (%(mxid)s) silindi. Beklenmedik bir durumsa, sunucu yöneticinizle iletişime geçin.", + "heading": "Hesap silindi" + }, + "locked": { + "description": "Bu hesap (%(mxid)s) kilitlendi. Bu beklenmedik bir durumsa, sunucu yöneticinizle iletişime geçin.", + "heading": "Hesap kilitlendi" + }, + "logged_out": { + "description": "Bu oturum sonlandırılmıştır. Tekrar giriş yapabilmek için lütfen oturumu kapatın.", + "heading": "Oturum sonlandırıldı" + } + }, + "back_to_homepage": "Ana sayfaya geri dön", + "captcha": { + "noscript": "Bu form bir CAPTCHA ile korunmaktadır ve gönderilebilmesi için JavaScript'in etkinleştirilmesi gerekmektedir. Lütfen tarayıcınızda JavaScript'i etkinleştirin ve bu sayfayı yeniden yükleyin." + }, + "change_password": { + "change": "Parola değiştir", + "confirm": "Şifreyi onayla", + "current": "Mevcut parola", + "heading": "Parolamı değiştir", + "new": "Yeni parola" + }, + "choose_display_name": { + "description": "Bu, diğer kişilerin göreceği isimdir. Bunu istediğiniz zaman değiştirebilirsiniz.", + "headline": "Görüntülenecek adınızı seçin" + }, + "consent": { + "continue_to": "%(client_name)s devam edilsin mi?", + "scope_list_preface": "Devam ederek, %(client_name)s'in şunları yapmasına izin vermiş oluyorsunuz:", + "this_will_setup": "Bu işlem, %(client_name)s (%(client_uri)s) cihazını %(server_name)s hesabınızla eşleştirecektir.", + "use_another_account": "Başka bir hesap kullanın" + }, + "device_card": { + "access_requested": "Erişim talebi", + "generic_device": "Cihaz", + "ip_address": "IP Adresi", + "security_code": "Güvenlik kodu" + }, + "device_code_link": { + "description": "Diğer cihazınızda gösterilen güvenlik kodunu girin", + "headline": "Hesabınıza yeni bir cihaz bağlayın", + "verification_code": "Doğrulama kodu" + }, + "device_consent": { + "confirm_device": "Evet, bunun benim cihazım olduğunu ve oturum açmak istediğimi onaylıyorum.", + "denied": { + "description": "%(client_name)s'e erişimi reddettiniz. Bu pencereyi kapatabilirsiniz.", + "heading": "Erişim engellendi" + }, + "description": " Başka bir cihaz olan , %(client_name)s cihazını , %(server_name)s, hesabınızla bağlantı istiyor. Bu cihazı tanıdığınızdan emin olun.", + "grant_access": "Erişim izni ver", + "granted": { + "description": "%(client_name)s kullanıcısına erişim izni verdiniz. Bu pencereyi kapatabilirsiniz.", + "heading": "Erişim izni verildi" + }, + "title": "Hesabınıza erişim izni verecek misiniz?", + "warning": { + "description": "Bu cihazın gerçekten sizin cihazınız olduğundan emin misiniz? Yöneticiler veya BT destek ekibi sizden bunu kabul etmenizi asla istemez.", + "title": "Hesabınıza uzaktan erişim sağlayan bir cihaza erişim izni vermek üzeresiniz" + } + }, + "device_display_name": { + "client_on_device": "%(client_name)s, %(device_name)s üzerinde", + "name_for_platform": "%(name)s / %(platform)s", + "unknown_device": "Bilinmeyen cihaz" + }, + "email_in_use": { + "description": "Hesap bilgilerinizi unuttuysanız, hesabınızı kurtarabilirsiniz. Ayrıca sıfırdan başlayıp farklı bir e-posta adresi de kullanabilirsiniz.", + "title": "%(email)s e-posta adresi zaten kullanımda." + }, + "emails": { + "greeting": "Merhaba %(username)s,", + "recovery": { + "click_button": "Yeni bir parola oluşturmak için aşağıdaki düğmeye tıklayın:", + "copy_link": "Yeni bir parola oluşturmak için aşağıdaki bağlantıyı kopyalayıp tarayıcınıza yapıştırın:", + "create_new_password": "Yeni parola oluştur", + "fallback": "Buton sizde çalışmıyor mu?", + "headline": "%(server_name)s hesabınız için parola sıfırlama talebinde bulundunuz.", + "subject": "Hesap parolanızı sıfırlayın (%(mxid)s)", + "you_can_ignore": "Yeni bir parola talep etmediyseniz, bu e-postayı dikkate almayın. Mevcut parolanız çalışmaya devam edecektir." + }, + "verify": { + "body_html": "Bu e-posta adresini doğrulamak için gereken doğrulama kodunuz: %(code)s", + "body_text": "Bu e-posta adresini doğrulamak için gereken doğrulama kodunuz: %(code)s", + "subject": "E-posta doğrulama kodunuz: %(code)s" + } + }, + "errors": { + "captcha": "CAPTCHA doğrulaması başarısız oldu, lütfen tekrar deneyin", + "denied_policy": "Politika gereği reddedildi: %(policy)s", + "email_banned": "Sunucu politikası gereği e-posta kullanımı yasaktır.", + "email_domain_banned": "E-posta etki alanı, sunucu politikası tarafından yasaklandı", + "email_domain_not_allowed": "E-posta alan adı sunucu politikası tarafından izin verilmiyor", + "email_not_allowed": "Sunucu politikası gereği e-posta gönderimine izin verilmiyor", + "field_required": "Bu alan zorunludur", + "invalid_credentials": "Geçersiz kimlik bilgileri", + "password_mismatch": "Parola alanları birbiriyle uyuşmuyor", + "rate_limit_exceeded": "Kısa süre içinde çok fazla istekte bulundunuz. Lütfen birkaç dakika bekleyin ve tekrar deneyin.", + "username_all_numeric": "Kullanıcı adı yalnızca rakamlardan oluşamaz", + "username_banned": "Kullanıcı adı sunucu politikası gereği yasaklanmıştır", + "username_invalid_chars": "Kullanıcı adı geçersiz karakterler içeriyor. Yalnızca küçük harfler, rakamlar, tire ve alt çizgi kullanın.", + "username_not_allowed": "Kullanıcı adı sunucu politikası gereği izin verilmiyor", + "username_taken": "Bu kullanıcı adı zaten alınmış", + "username_too_long": "Kullanıcı adı çok uzun", + "username_too_short": "Kullanıcı adı çok kısa" + }, + "legacy_consent": { + "this_will_setup": "Bu işlem, %(client_name)s cihazını %(server_name)s hesabınızla eşleştirecektir." + }, + "login": { + "call_to_register": "Henüz hesabınız yok mu?", + "continue_with_provider": "%(provider)s ile devam edin", + "description": "Devam etmek için lütfen giriş yapın:", + "forgot_password": "Parolanızı mı unuttunuz?", + "headline": "Giriş Yap", + "link": { + "description": "%(provider)s hesabınızı bağlama", + "headline": "Bağlantıya erişmek için giriş yapın" + }, + "no_login_methods": "Kullanılabilir giriş yöntemi yok.", + "username_or_email": "Kullanıcı adı veya E-posta" + }, + "navbar": { + "my_account": "Hesabım", + "register": "Hesap oluştur", + "signed_in_as": "%(username)s olarak giriş yapıldı." + }, + "not_found": { + "description": "Aradığınız sayfa mevcut değil veya taşınmış", + "heading": "Sayfa bulunamadı" + }, + "not_you": "%(username)s değil mi?", + "policy_violation": { + "description": "Bu durum, isteği oluşturan istemci, şu anda oturum açmış olan kullanıcı veya isteğin kendisinden kaynaklanıyor olabilir.", + "heading": "Yetkilendirme isteği, bu hizmet tarafından uygulanan politika nedeniyle reddedildi", + "logged_as": "%(username)s olarak kaydedildi.", + "too_many_sessions": { + "description": "Hesabınız maksimum sayıda cihazda oturum açmış durumda. Devam etmek için, mevcut cihazlarınızdan %(num_devices_to_remove)s kaldırın ve tekrar oturum açın.", + "heading": "Cihaz limitine ulaşıldı", + "manage_devices": "Cihazları yönetin" + } + }, + "recovery": { + "consumed": { + "description": "Yeni bir parola oluşturmak için baştan başlayın ve \"Parolamı unuttum\" seçeneğini seçin.", + "heading": "Parolanızı sıfırlamak için kullanılan bağlantı daha önce kullanıldı" + }, + "disabled": { + "description": "Giriş bilgilerinizi kaybettiyseniz, hesabınızı kurtarmak için lütfen yöneticiyle iletişime geçin.", + "heading": "Hesap kurtarma devre dışı bırakıldı." + }, + "expired": { + "description": "Aşağıdaki adreslere gönderilecek yeni bir e-posta isteği gönderin: %(email)s.", + "heading": "Parolanızı sıfırlama bağlantısının süresi doldu" + }, + "finish": { + "confirm": "Yeni parolayı tekrar girin.", + "description": "Hesabınız için yeni bir parola belirleyin.", + "heading": "Parolanızı sıfırlayın", + "new": "Yeni parola", + "save_and_continue": "Kaydet ve devam et" + }, + "progress": { + "change_email": "Farklı bir e-posta adresi deneyin", + "description": "%(email)s kullanan bir hesabınız varsa, parolanızı sıfırlamak için bağlantı içeren bir e-posta gönderdik.", + "heading": "E-postalarınızı kontrol edin" + }, + "start": { + "description": "Parolanızı sıfırlamak için bağlantı içeren bir e-posta gönderilecektir.", + "heading": "Devam etmek için e-postanızı girin." + } + }, + "register": { + "call_to_login": "Zaten hesabınız var mı?", + "continue_with_email": "E-posta adresiyle devam edin", + "continue_with_password": "Parolayla devam et", + "create_account": { + "description": "Devam etmek için bir kullanıcı adı seçin.", + "heading": "Hesap oluştur" + }, + "terms_of_service": " Şartları ve Koşulları kabul ediyorum" + }, + "registration_token": { + "description": "Ana sunucu yöneticisi tarafından sağlanan bir kayıt belirteci girin.", + "field": "Kayıt anahtarı", + "headline": "Kayıt anahtarı" + }, + "scope": { + "edit_profile": "Profilinizi ve iletişim bilgilerinizi düzenleyin", + "manage_sessions": "Cihazlarınızı ve oturumlarınızı yönetin", + "mas_admin": "Kullanıcıları yönet (urn:mas:admin)", + "send_messages": "Sizin adınıza mesaj gönderin", + "synapse_admin": "Sunucuyu yönetin (urn:synapse:admin:*)", + "view_messages": "Mevcut mesajları ve verilerinizi görüntüleyinmessages", + "view_profile": "Profil bilgilerinizi ve iletişim bilgilerinizi görüntüleyin" + }, + "upstream_oauth2": { + "link_mismatch": { + "heading": "Bu üst hesap zaten başka bir hesaba bağlı." + }, + "register": { + "choose_username": { + "description": "Bu daha sonra değiştirilemez." + }, + "create_account": "Yeni hesap oluşturun", + "enforced_by_policy": "Sunucu politikası tarafından uygulanır", + "forced_display_name": "Aşağıdaki görünen adı kullanacağız", + "forced_email": "Aşağıdaki e-posta adresini kullanacağım", + "forced_localpart": "Aşağıdaki kullanıcı adını kullanacağım", + "import_data": { + "description": "Yeni %(server_name)s hesabınızla ilişkilendirilecek bilgileri onaylayın.", + "heading": "Verilerinizi içe aktarın" + }, + "imported_from_upstream": "Ana hesabınızdan içe aktarıldı", + "imported_from_upstream_with_name": "%(human_name)s hesabınızdan içe aktarıldı", + "link_existing": "Mevcut bir hesaba bağlantı", + "provider_name": "%(human_name)s hesabı", + "signup_with_upstream": { + "heading": "%(human_name)s hesabınızla kaydolmaya devam edin" + }, + "suggested_display_name": "İçe aktarılan görünen ad", + "suggested_email": "E-posta adresini içe aktar", + "use": "Kullan" + }, + "suggest_link": { + "action": "Bağlantı", + "heading": "Mevcut hesabınıza bağlanın" + } + }, + "verify_email": { + "6_digit_code": "6 haneli kod", + "description": "Gönderilen 6 haneli kodu girin: %(email)s", + "headline": "E-postanızı doğrulayın" + } + } +} \ No newline at end of file diff --git a/translations/uk.json b/translations/uk.json index d347b16e1..aba940519 100644 --- a/translations/uk.json +++ b/translations/uk.json @@ -39,11 +39,11 @@ "mas": { "account": { "deactivated": { - "description": "Цей обліковий запис (%(mxid)s) видалено. Якщо цього не передбачалося, зверніться до адміністратора сервера.", + "description": "Цей обліковий запис (%(mxid)s) видалено. Якщо цього не передбачалося, зверніться до постачальника вашого облікового запису.", "heading": "Обліковий запис видалено" }, "locked": { - "description": "Цей обліковий запис (%(mxid)s) заблоковано. Якщо цього не передбачалося, зверніться до адміністратора сервера.", + "description": "Цей обліковий запис (%(mxid)s) заблоковано. Якщо цього не передбачалося, зверніться до постачальника вашого облікового запису.", "heading": "Обліковий запис заблоковано" }, "logged_out": { @@ -226,7 +226,7 @@ "terms_of_service": "Я погоджуюся з Умовами та положеннями" }, "registration_token": { - "description": "Введіть реєстраційний токен, наданий адміністратором домашнього сервера.", + "description": "Введіть реєстраційний токен, наданий постачальником вашого облікового запису.", "field": "Токен реєстрації", "headline": "Токен реєстрації" }, diff --git a/translations/uz.json b/translations/uz.json index e18523f0a..a8afc9534 100644 --- a/translations/uz.json +++ b/translations/uz.json @@ -5,7 +5,9 @@ "continue": "Davom etish", "create_account": "Ro'yxatdan o'tish", "sign_in": "Kirish", - "sign_out": "Chiqish" + "sign_out": "Chiqish", + "skip": "Oʻtkazib yuborish", + "start_over": "Qaytadan boshlang" }, "app": { "human_name": "Matrix Authentication Service", @@ -35,6 +37,20 @@ "unexpected": "Kutilmagan xato" }, "mas": { + "account": { + "deactivated": { + "description": "Ushbu hisob (%(mxid)s) oʻchirildi. Agar bu kutilmasa, server administratoringiz bilan bogʻlaning.", + "heading": "Hisob oʻchirildi" + }, + "locked": { + "description": "Ushbu hisob (%(mxid)s) qulflangan. Agar bu kutilmasa, server administratoringizga murojaat qiling.", + "heading": "Hisob qulflangan" + }, + "logged_out": { + "description": "Bu sessiya tugatildi. Qayta kirish uchun tizimdan chiqing", + "heading": "Sessiya tugadi" + } + }, "back_to_homepage": "Bosh sahifaga qaytish", "captcha": { "noscript": "Bu shakl CAPTCHA bilan himoyalangan va uni yuborish uchun JavaScript yoqilishi kerak. Brauzeringizda JavaScript’ni yoqing va bu sahifani qayta yuklang." @@ -46,30 +62,65 @@ "heading": "Parolni o‘zgartirish", "new": "Yangi parol" }, + "choose_display_name": { + "description": "Bu boshqa odamlar koʻradigan nom. Buni istalgan vaqtda oʻzgartirishingiz mumkin.", + "headline": "Koʻrsatiladigan nomingizni tanlang" + }, + "consent": { + "continue_to": "%(client_name)s ga oʻtishni xohlaysizmi?", + "scope_list_preface": "Davom etish orqali siz %(client_name)s ga quyidagilarga ruxsat berasiz:", + "this_will_setup": "Bu sizning %(server_name)s hisobingiz bilan %(client_name)s (%(client_uri)s) ni oʻrnatadi.", + "use_another_account": "Boshqa hisobdan foydalaning" + }, "device_card": { "access_requested": "Ruxsat so‘raldi", "generic_device": "Qurilma", - "ip_address": "IP manzili" + "ip_address": "IP manzili", + "security_code": "Xavfsizlik kodi" }, "device_code_link": { - "description": "Qurilmani ulang", - "headline": "Qurilmangizda chiqqan kodni kiriting" + "description": "Boshqa qurilmangizda koʻrsatilgan xavfsizlik kodini kiriting", + "headline": "Qurilmangizda chiqqan kodni kiriting", + "verification_code": "Tasdiqlash kodi" }, "device_consent": { + "confirm_device": "Ha, men bu mening qurilmam ekanligini tasdiqlayman va unga kirishni xohlayman.", "denied": { "description": "Siz %(client_name)s uchun ruxsatni rad etdingiz. Bu oynani yopishingiz mumkin.", "heading": "Ruxsat berilmadi" }, + "description": "Boshqa qurilma %(client_name)s ni sizning %(server_name)s hisobingiz bilan bogʻlamoqchi. Ushbu qurilmani tanib olganingizga ishonch hosil qiling.", + "grant_access": "Kirishga ruxsat berish", "granted": { "description": "Siz %(client_name)s uchun ruxsat berdingiz. Bu oynani yopishingiz mumkin.", "heading": "Ruxsat berildi" + }, + "title": "Hisobingizga kirish huquqi berilsinmi?", + "warning": { + "description": "Bu sizning qurilmangiz ekanligiga aminmisiz? Administratorlar yoki IT qoʻllab-quvvatlash xizmati sizdan buni qabul qilishingizni hech qachon soʻramaydi.", + "title": "Siz hisobingizga masofaviy qurilmaga kirish huquqini bermoqchisiz" } }, "device_display_name": { + "client_on_device": "%(client_name)s ustida %(device_name)s", + "name_for_platform": "%(platform)s uchun %(name)s", "unknown_device": "Noma’lum qurilma." }, + "email_in_use": { + "description": "Agar hisob qaydnomangiz maʻlumotlarini unutgan boʻlsangiz, hisobingizni tiklashingiz mumkin. Shuningdek, qayta boshlashingiz va boshqa elektron pochta manzilidan foydalanishingiz mumkin.", + "title": "%(email)s elektron pochta manzili allaqachon ishlatilmoqda" + }, "emails": { "greeting": "Salom %(username)s ,", + "recovery": { + "click_button": "Yangi parol yaratish uchun quyidagi tugmani bosing:", + "copy_link": "Yangi parol yaratish uchun quyidagi havolani nusxalang va brauzerga joylashtiring:", + "create_new_password": "Yangi parol yarating", + "fallback": "Tugma siz uchun ishlamayaptimi?", + "headline": "Siz %(server_name)s hisobingiz uchun parolni qayta tiklashni soʻradingiz.", + "subject": "Hisob parolingizni qayta oʻrnating (%(mxid)s)", + "you_can_ignore": "Agar siz yangi parol soʻramagan boʻlsangiz, ushbu elektron pochta xabarini eʻtiborsiz qoldirishingiz mumkin. Joriy parolingiz ishlashda davom etadi." + }, "verify": { "body_html": "Ushbu elektron pochta manzilini tasdiqlash uchun tasdiqlash kodingiz: %(code)s", "body_text": "Ushbu elektron pochta manzilini tasdiqlash uchun tasdiqlash kodingiz: %(code)s", @@ -79,21 +130,37 @@ "errors": { "captcha": "CAPTCHA tekshiruvi amalga oshmadi, qayta urining", "denied_policy": "Siyosat tomonidan rad etilgan: %(policy)s", + "email_banned": "Elektron pochta server siyosati tomonidan taqiqlangan", + "email_domain_banned": "Elektron pochta domeni server siyosati tomonidan taqiqlangan", + "email_domain_not_allowed": "Elektron pochta domeni server siyosati tomonidan ruxsat etilmagan", + "email_not_allowed": "Server siyosati elektron pochtaga ruxsat bermagan", "field_required": "Ushbu qator toʻldirilishi shart", "invalid_credentials": "Hisob ma’lumotlari yaroqsiz", "password_mismatch": "Parol maydonlari mos kelmayapti", - "username_taken": "Bu foydalanuvchi nomi allaqachon band" + "rate_limit_exceeded": "Qisqa vaqt ichida juda koʻp soʻrov yubordingiz. Iltimos, bir necha daqiqa kuting va qayta urinib koʻring.", + "username_all_numeric": "Foydalanuvchi nomi faqat raqamlardan iborat boʻlmasligi kerak", + "username_banned": "Foydalanuvchi nomi server siyosati tomonidan taqiqlangan", + "username_invalid_chars": "Foydalanuvchi nomida yaroqsiz belgilar mavjud. Faqat kichik harflar, raqamlar, tire va pastki chiziqlardan foydalaning.", + "username_not_allowed": "Foydalanuvchi nomi server siyosati tomonidan ruxsat etilmagan", + "username_taken": "Bu foydalanuvchi nomi allaqachon band", + "username_too_long": "Foydalanuvchi nomi juda uzun", + "username_too_short": "Foydalanuvchi nomi juda qisqa" + }, + "legacy_consent": { + "this_will_setup": "Bu sizning %(server_name)s hisobingiz bilan %(client_name)s ni oʻrnatadi." }, "login": { "call_to_register": "Hali hisobingiz yo‘qmi?", "continue_with_provider": "%(provider)s bilan davom etish", "description": "Davom etish uchun tizimga kiring:", + "forgot_password": "Parolni unutdingizmi?", "headline": "Kirish", "link": { "description": "%(provider)s hisobingiz ulanmoqda", "headline": "Havola uchun tizimga kiring" }, - "no_login_methods": "Kirish usullari mavjud emas." + "no_login_methods": "Kirish usullari mavjud emas.", + "username_or_email": "Foydalanuvchi nomi yoki elektron pochta manzili" }, "navbar": { "my_account": "Mening hisobim", @@ -109,28 +176,60 @@ "policy_violation": { "description": "Bu so‘rovni yaratgan mijoz, hozirda tizimga kirgan foydalanuvchi yoki so‘rovning o‘zi bilan bog‘liq bo‘lishi mumkin.", "heading": "Avtorizatsiya so‘rovi ushbu xizmatda amalda bo‘lgan siyosat tufayli rad etildi.", - "logged_as": "%(username)s sifatida kirildi" + "logged_as": "%(username)s sifatida kirildi", + "too_many_sessions": { + "description": "Hisobingiz allaqachon maksimal miqdordagi qurilmalarda tizimga kirgan. Davom etish uchun mavjud qurilmalaringizdan %(num_devices_to_remove)s ni olib tashlang va qaytadan kiring.", + "heading": "Qurilma limitiga yetdi", + "manage_devices": "Qurilmalarni boshqarish" + } }, "recovery": { + "consumed": { + "description": "Yangi parol yaratish uchun boshidan boshlang va ʼʼParolni unutdingizmi“ ni tanlang.", + "heading": "Parolingizni tiklash havolasi allaqachon ishlatilgan" + }, + "disabled": { + "description": "Agar hisob maʻlumotlaringizni yoʻqotib qoʻysangiz, hisobingizni tiklash uchun administrator bilan bogʻlaning.", + "heading": "Hisobni tiklash oʻchirib qoʻyilgan" + }, "expired": { + "description": "Quyidagi manzilga yuboriladigan yangi elektron pochta xabarini soʻrang: %(email)s.", + "heading": "Parolingizni tiklash havolasi muddati tugagan", "resend_email": "Elektron pochtani qayta yuborish" }, "finish": { + "confirm": "Yangi parolni qayta kiriting", "description": "Hisobingiz uchun yangi parol tanlang.", - "new": "Yangi parol" + "heading": "Parolingizni qayta tiklash", + "new": "Yangi parol", + "save_and_continue": "Saqlash va davom ettirish" }, "progress": { + "change_email": "Boshqa elektron pochta manzilini sinab koʻring", + "description": "Agar %(email)s dan foydalanadigan hisob mavjud boʻlsa, parolingizni tiklash uchun havola bilan elektron pochta xabarini yubordik.", + "heading": "Elektron pochtangizni tekshiring", "resend_email": "Elektron pochtani qayta yuborish" + }, + "start": { + "description": "Parolingizni tiklash uchun havola bilan elektron pochta xabari yuboriladi.", + "heading": "Davom etish uchun elektron pochta manzilingizni kiriting" } }, "register": { "call_to_login": "Hisobingiz allaqachon bormi?", + "continue_with_email": "Elektron pochta manzili bilan davom eting", + "continue_with_password": "Parol bilan davom eting", "create_account": { "description": "Davom etish uchun foydalanuvchi nomini tanlang.", "heading": "Hisob yaratish" }, "terms_of_service": "Men roziman Foydalanish shartlari" }, + "registration_token": { + "description": "Homeserver administratori tomonidan taqdim etilgan roʻyxatdan oʻtish tokenini kiriting.", + "field": "Roʻyxatdan oʻtish tokeni", + "headline": "Roʻyxatdan oʻtish tokeni" + }, "scope": { "edit_profile": "Profilingiz va aloqa maʼlumotlaringizni tahrirlang", "manage_sessions": "Qurilmalaringiz va sessiyalaringizni boshqaring", @@ -159,7 +258,12 @@ "heading": "Ma’lumotlaringizni import qiling" }, "imported_from_upstream": "Yuqori darajadagi hisobingizdan import qilindi", + "imported_from_upstream_with_name": "%(human_name)s hisobingizdan import qilindi", "link_existing": "Mavjud hisobga havola", + "provider_name": "%(human_name)s hisobi", + "signup_with_upstream": { + "heading": "%(human_name)s hisobingiz bilan roʻyxatdan oʻtishda davom eting" + }, "suggested_display_name": "Ko‘rsatish nomini import qilish", "suggested_email": "Elektron pochta manzilini import qilish", "use": "Foydalanish" diff --git a/translations/zh-Hans.json b/translations/zh-Hans.json index c5b34bd3d..4f68ea2b4 100644 --- a/translations/zh-Hans.json +++ b/translations/zh-Hans.json @@ -79,7 +79,7 @@ "security_code": "安全代码" }, "device_code_link": { - "description": "关联设备", + "description": "输入你另一设备上显示的安全代码", "headline": "关联新设备到账户", "verification_code": "验证码" },